<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Pritesh Bhanushali]]></title><description><![CDATA[Pritesh Bhanushali]]></description><link>https://newsletter.priteshbhanushali.com</link><image><url>https://substackcdn.com/image/fetch/$s_!DiOV!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92a0aac7-a3c0-44be-97d4-f77aaa58306c_593x593.png</url><title>Pritesh Bhanushali</title><link>https://newsletter.priteshbhanushali.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 03 Nov 2026 10:06:44 GMT</lastBuildDate><atom:link href="https://newsletter.priteshbhanushali.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Pritesh Bhanushali]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[devpritesh@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[devpritesh@substack.com]]></itunes:email><itunes:name><![CDATA[Pritesh Bhanushali]]></itunes:name></itunes:owner><itunes:author><![CDATA[Pritesh Bhanushali]]></itunes:author><googleplay:owner><![CDATA[devpritesh@substack.com]]></googleplay:owner><googleplay:email><![CDATA[devpritesh@substack.com]]></googleplay:email><googleplay:author><![CDATA[Pritesh Bhanushali]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[🧩 Stop Passing Arrays Everywhere: Use DTOs in Laravel]]></title><description><![CDATA[Laravel DTOs: Improve Type Safety, Testability, and Code Organization]]></description><link>https://newsletter.priteshbhanushali.com/p/stop-passing-arrays-everywhere-use</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/stop-passing-arrays-everywhere-use</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Mon, 02 Nov 2026 05:30:59 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3029db1e-f47a-4ded-82cd-6dc792129628_1309x1201.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever wondered how to keep your Laravel controllers clean, your services reusable, and your application data properly structured?</p><p>As Laravel applications grow, passing arrays between controllers and services can make code harder to understand and maintain. Services may depend on loosely defined data structures, and developers may struggle to determine which fields an operation actually requires.</p><p>This is where <strong>DTOs (Data Transfer Objects)</strong> become useful.</p><p>DTOs help you define clear data contracts between different parts of your application. Instead of passing an entire HTTP request or an unstructured array, you can pass an object that explicitly represents the required data.</p><p>In this article, we&#8217;ll explore:</p><ul><li><p>What a DTO is and why it matters.</p></li><li><p>The benefits and limitations of using DTOs.</p></li><li><p>How to implement a DTO in Laravel using a student registration example.</p></li><li><p>How to integrate DTOs with Laravel validation and services.</p></li><li><p>How Spatie Laravel Data simplifies DTO implementation.</p></li><li><p>When to choose plain PHP DTOs versus Spatie Laravel Data.</p></li></ul><p>Let&#8217;s get started.</p><h2>&#128232; What Is a DTO?</h2><p>A <strong>Data Transfer Object (DTO)</strong> is an object designed to carry data between different parts of an application.</p><p>Its primary responsibility is to define the structure of the data being transferred, rather than perform the business operation itself.</p><p>Consider a student registration application built with Laravel.</p><p>When a student registers for a course, the application might need the following information:</p><ul><li><p>Student name</p></li><li><p>Email address</p></li><li><p>Age</p></li><li><p>Course ID</p></li></ul><p>We could pass this information as an associative array:</p><pre><code><code>$studentData = [
    'name' =&gt; 'Rahul Sharma',
    'email' =&gt; 'rahul@example.com',
    'age' =&gt; 21,
    'course_id' =&gt; 1,
];</code></code></pre><p>This works, but the expected structure is implicit. Nothing in the array itself tells us which fields are mandatory or what types they should have.</p><p>We can represent the same information using a DTO:</p><pre><code><code>$studentData = new StudentRegistrationData(
    name: 'Rahul Sharma',
    email: 'rahul@example.com',
    age: 21,
    courseId: 1,
);</code></code></pre><p>Now the data is represented by a defined class with explicitly named properties and types.</p><p><strong>The key idea:</strong> A DTO makes the data contract explicit and provides a structured way to transfer data between application layers.</p><div><hr></div><h2>&#128161; Why Do We Need DTOs in Laravel?</h2><p>Laravel makes it easy to access request data:</p><pre><code><code>$request-&gt;input('name');
$request-&gt;input('email');</code></code></pre><p>For a simple CRUD operation, this may be sufficient.</p><p>However, as an application grows, controllers and services can become tightly coupled to HTTP requests or depend on arrays with inconsistent structures.</p><p>Consider this service method:</p><pre><code><code>public function register(array $data): Student
{
    // Registration logic
}</code></code></pre><p>What fields does <code>$data</code> require? Is <code>age</code> an integer? Is <code>course_id</code> mandatory? Does the service need the student&#8217;s email or the entire request?</p><p>The method signature doesn&#8217;t tell us.</p><p>Now consider this version:</p><pre><code><code>public function register(
    StudentRegistrationData $data
): Student {
    // Registration logic
}</code></code></pre><p>The method explicitly declares the type of data it expects.</p><p>DTOs are especially useful when:</p><ul><li><p>Services contain complex business logic.</p></li><li><p>Multiple application components call the same service.</p></li><li><p>Data comes from APIs, imports, or external integrations.</p></li><li><p>Queued jobs need structured input.</p></li><li><p>You want to test services independently of HTTP requests.</p></li><li><p>Your application benefits from clear boundaries between layers.</p></li></ul><div><hr></div><h2>&#127919; What Are the Benefits of DTOs?</h2><h4>&#9989; Clear data contracts</h4><p>A DTO explicitly defines which fields an operation expects.</p><p>Developers can understand the structure of the data by reading the class rather than searching through several controllers and array definitions.</p><h4>&#9989; Better separation of concerns</h4><p>Controllers handle HTTP-related responsibilities, DTOs carry data, and services coordinate business operations.</p><p>Each component has a clearer purpose.</p><h4>&#9989; Improved type safety</h4><p>Typed properties help detect certain programming mistakes and make the expected data types explicit.</p><p>For example:</p><pre><code><code>public function __construct(
    public string $name,
    public string $email,
    public int $age,
    public int $courseId,
) {}</code></code></pre><p>These declarations help communicate the intended structure, although they do not replace validation of untrusted input.</p><h4>&#9989; Easier unit testing</h4><p>You can construct a DTO directly in a test without creating an HTTP request.</p><p>This makes it easier to test service behavior independently of the controller.</p><h4>&#9989; Reusability</h4><p>The same service can receive a DTO from a controller, queued job, console command, or another service.</p><p>The service does not need to know where the data originated.</p><h4>&#9989; Controlled data transfer</h4><p>You can explicitly map only the fields needed for an operation. This reduces the risk of accidentally passing unrelated request fields into business logic or database operations.</p><div><hr></div><h2>&#128736;&#65039; Implementing a DTO in Laravel Using Student Registration</h2><p>Let&#8217;s build a practical student registration workflow using plain PHP DTOs.</p><p>The flow will be:</p><p><strong>HTTP Request &#8594; Validation &#8594; DTO &#8594; Service &#8594; Database</strong></p><p>We&#8217;ll use a Form Request for validation, a DTO to represent the registration data, a service to handle persistence, and a controller to coordinate the process.</p><h3>Step 1: Create the DTO class</h3><p>Create the following file:</p><p><code>app/DTOs/StudentRegistrationData.php</code></p><pre><code><code>&lt;?php

namespace App\DTOs;

final readonly class StudentRegistrationData
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
        public int $courseId,
    ) {}

    public static function fromArray(array $data): self
    {
        return new self(
            name: $data['name'],
            email: $data['email'],
            age: (int) $data['age'],
            courseId: (int) $data['course_id'],
        );
    }

    public function toArray(): array
    {
        return [
            'name' =&gt; $this-&gt;name,
            'email' =&gt; $this-&gt;email,
            'age' =&gt; $this-&gt;age,
            'course_id' =&gt; $this-&gt;courseId,
        ];
    }
}</code></code></pre><p>Let&#8217;s understand the important parts.</p><p><code>final</code> prevents the class from being extended.</p><p><code>readonly</code> prevents the properties from being reassigned after initialization. This syntax requires PHP 8.2 or later.</p><p><code>__construct()</code> defines the data the DTO requires.</p><p><code>fromArray()</code> converts an associative array into a DTO.</p><p><code>toArray()</code> converts the DTO into an array when needed.</p><p>Notice that the DTO contains no database queries or registration workflow. Its responsibility is to represent the registration data.</p><p>Also, the type casts in <code>fromArray()</code> are not validation. We will validate incoming values before creating the DTO.</p><h3>Step 2: Create a Form Request for validation</h3><p>Run the following command:</p><pre><code><code>php artisan make:request StudentRegistrationRequest</code></code></pre><p>Update <code>app/Http/Requests/StudentRegistrationRequest.php</code>:</p><pre><code><code>&lt;?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StudentRegistrationRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' =&gt; [
                'required',
                'string',
                'max:255',
            ],
            'email' =&gt; [
                'required',
                'email',
                'max:255',
                'unique:students,email',
            ],
            'age' =&gt; [
                'required',
                'integer',
                'min:16',
                'max:100',
            ],
            'course_id' =&gt; [
                'required',
                'integer',
                'exists:courses,id',
            ],
        ];
    }
}</code></code></pre><p>The Form Request validates incoming data before the controller action executes.</p><p>For this example:</p><ul><li><p><code>name</code> must be a non-empty string.</p></li><li><p><code>email</code> must be valid and unique in the students table.</p></li><li><p><code>age</code> must be between 16 and 100.</p></li><li><p><code>course_id</code> must reference an existing course.</p></li></ul><p>These age limits are example business rules and should be adjusted to suit the application.</p><p>In a real application, also implement the necessary authorization checks to ensure that the student is permitted to register for the selected course.</p><h3>Step 3: Create the registration service</h3><p>Create <code>app/Services/StudentRegistrationService.php</code>:</p><pre><code><code>&lt;?php

namespace App\Services;

use App\DTOs\StudentRegistrationData;
use App\Models\Student;

class StudentRegistrationService
{
    public function register(
        StudentRegistrationData $data
    ): Student {
        return Student::create(
            $data-&gt;toArray()
        );
    }
}</code></code></pre><p>The service accepts a <code>StudentRegistrationData</code> object rather than an HTTP request.</p><p>This makes it independent of the HTTP layer.</p><p>For example, a queued job could construct the same DTO and call this service without creating an HTTP request.</p><p>In a larger registration system, the service might also coordinate enrollment rules, seat availability, and notifications.</p><h3>Step 4: Use the DTO in the controller</h3><p>Create the controller if it doesn&#8217;t already exist:</p><pre><code><code>php artisan make:controller StudentController</code></code></pre><p>Update <code>app/Http/Controllers/StudentController.php</code>:</p><pre><code><code>&lt;?php

namespace App\Http\Controllers;

use App\DTOs\StudentRegistrationData;
use App\Http\Requests\StudentRegistrationRequest;
use App\Services\StudentRegistrationService;
use Illuminate\Http\JsonResponse;

class StudentController extends Controller
{
    public function store(
        StudentRegistrationRequest $request,
        StudentRegistrationService $service
    ): JsonResponse {
        $data = StudentRegistrationData::fromArray(
            $request-&gt;validated()
        );

        $student = $service-&gt;register($data);

        return response()-&gt;json([
            'message' =&gt; 'Student registered successfully',
            'data' =&gt; $student,
        ], 201);
    }
}</code></code></pre><p>The controller now coordinates the process without containing the database operation itself.</p><p>Its responsibilities are straightforward:</p><ol><li><p>Receive validated input.</p></li><li><p>Convert the input into a DTO.</p></li><li><p>Pass the DTO to the service.</p></li><li><p>Return an HTTP response.</p></li></ol><p>Laravel&#8217;s service container automatically resolves the service dependency.</p><h3>Step 5: Register the API route</h3><p>Add this to <code>routes/api.php</code>:</p><pre><code><code>use App\Http\Controllers\StudentController;
use Illuminate\Support\Facades\Route;

Route::post('/students', [
    StudentController::class,
    'store',
]);</code></code></pre><p>Make sure the <code>Student</code> model, database migration, and <code>Course</code> model exist.</p><p>The <code>Student</code> model should permit the intended fields through its <code>$fillable</code> property:</p><pre><code><code>protected $fillable = [
    'name',
    'email',
    'age',
    'course_id',
];</code></code></pre><h3>Step 6: Test the endpoint</h3><p>Send a POST request to <code>/api/students</code> with the following JSON body:</p><pre><code><code>{
    "name": "Rahul Sharma",
    "email": "rahul@example.com",
    "age": 21,
    "course_id": 1
}</code></code></pre><p>If validation passes and the database operation succeeds, the endpoint returns a <code>201 Created</code> response.</p><p>If the email is invalid, the age is outside the configured range, or the course does not exist, Laravel rejects the request during validation.</p><p>We now have a registration workflow in which the request, DTO, service, and database responsibilities are clearly separated.</p><div><hr></div><h2>&#9878;&#65039; DTOs vs. Arrays vs. Eloquent Models</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MWbE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MWbE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!MWbE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!MWbE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!MWbE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!MWbE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1643896,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/222627772?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MWbE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!MWbE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!MWbE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!MWbE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39648d7a-3a93-4c75-b673-84e503a4f756_1536x1024.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128268; Using Spatie Laravel Data: Taking DTOs to the Next Level</h2><p>So far, we&#8217;ve implemented a DTO using plain PHP. This approach is straightforward, but we had to write our own <code>fromArray()</code> and <code>toArray()</code> methods and define validation rules separately.</p><p>As an application grows, this mapping code can become repetitive.</p><p>What if we could reduce some of that boilerplate and gain additional functionality?</p><p>This is where <strong>Spatie Laravel Data</strong> comes in.</p><p>Spatie Laravel Data is a package that builds on the DTO concept and provides functionality for data transformation, validation, casting, and serialization. It also supports features such as nested data objects and TypeScript generation.</p><h3>&#9989; Install Spatie Laravel Data</h3><p>Install the package with Composer:</p><pre><code><code>composer require spatie/laravel-data</code></code></pre><p>The following example uses the package&#8217;s version 4 API.</p><h3>&#9989; Create a Student Registration Data object</h3><p>Create <code>app/Data/StudentRegistrationData.php</code>:</p><pre><code><code>&lt;?php

namespace App\Data;

use Spatie\LaravelData\Data;

class StudentRegistrationData extends Data
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
        public int $course_id,
    ) {}

    public static function rules(): array
    {
        return [
            'name' =&gt; [
                'required',
                'string',
                'max:255',
            ],
            'email' =&gt; [
                'required',
                'email',
                'max:255',
                'unique:students,email',
            ],
            'age' =&gt; [
                'required',
                'integer',
                'min:16',
                'max:100',
            ],
            'course_id' =&gt; [
                'required',
                'integer',
                'exists:courses,id',
            ],
        ];
    }
}</code></code></pre><p>The data structure and its validation rules are now defined in the same class.</p><p>The package can infer basic validation rules from property types, while custom rules let us express requirements such as email uniqueness and course existence.</p><h3>&#9989; Use the Data object in the controller</h3><p>With Spatie Laravel Data, you can inject the data object directly into a controller action.</p><pre><code><code>&lt;?php

namespace App\Http\Controllers;

use App\Data\StudentRegistrationData;
use App\Models\Student;
use Illuminate\Http\JsonResponse;

class StudentController extends Controller
{
    public function store(
        StudentRegistrationData $data
    ): JsonResponse {
        $student = Student::create(
            $data-&gt;toArray()
        );

        return response()-&gt;json([
            'message' =&gt; 'Student registered successfully',
            'data' =&gt; $student,
        ], 201);
    }
}</code></code></pre><p>When the data object is resolved from the request, the package can validate the incoming data and create the object before the controller action executes.</p><p>In this version of the example, Spatie Laravel Data handles the request validation, so a separate Form Request is not needed for the same validation rules.</p><p>If you require more complex authorization or already have a Form Request, you can retain that approach and construct the data object from validated input instead.</p><p>As before, ensure the model&#8217;s <code>$fillable</code> configuration and database schema match the fields being persisted.</p><div><hr></div><h3>&#11088; What are the benefits of Spatie Laravel Data?</h3><p><strong>1. Less boilerplate code</strong></p><p>The package reduces the need to manually implement common data conversion and transformation methods.</p><p><strong>2. Built-in validation support</strong></p><p>You can define validation rules alongside your data structure and use the package&#8217;s request integration.</p><p><strong>3. Automatic data transformation</strong></p><p>Data objects can be created from supported input sources and transformed into arrays or JSON.</p><p><strong>4. Type casting</strong></p><p>The package supports casting values into appropriate types, including configured or supported date-related casts.</p><p><strong>5. Consistent data contracts</strong></p><p>A data object can be reused across controllers, services, and API responses, reducing duplicated definitions.</p><p><strong>6. API response transformation</strong></p><p>The package supports transforming data objects for output and controlling which properties are included.</p><p><strong>7. TypeScript integration</strong></p><p>You can generate TypeScript definitions from data objects, which is particularly useful when working with React or other TypeScript-based frontends.</p><div><hr></div><h3>&#9878;&#65039; Plain PHP DTO vs. Spatie Laravel Data</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4B1Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4B1Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 424w, https://substackcdn.com/image/fetch/$s_!4B1Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 848w, https://substackcdn.com/image/fetch/$s_!4B1Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 1272w, https://substackcdn.com/image/fetch/$s_!4B1Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!4B1Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png" width="1374" height="1145" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1145,&quot;width&quot;:1374,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1797695,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/222627772?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4B1Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 424w, https://substackcdn.com/image/fetch/$s_!4B1Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 848w, https://substackcdn.com/image/fetch/$s_!4B1Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 1272w, https://substackcdn.com/image/fetch/$s_!4B1Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58b4052c-5f63-4abc-9042-7e7f652e3104_1374x1145.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>&#129300; Should you always use Spatie Laravel Data?</h3><p>Not necessarily.</p><p>Plain PHP DTOs are a good choice when you need a simple data structure and want to avoid additional dependencies.</p><p>Spatie Laravel Data becomes particularly useful when an application has complex request payloads, nested objects, multiple API response structures, and repeated data transformation requirements.</p><p>The package does not replace the DTO concept. It provides a more feature-rich way to implement data objects in Laravel.</p><div><hr></div><h2>&#11088; DTO Best Practices</h2><h4>&#128073; Keep DTOs focused</h4><p>Create DTOs around meaningful operations, such as <code>StudentRegistrationData</code>, rather than creating a generic object that contains every possible student field.</p><h4>&#128073; Keep business logic out of DTOs</h4><h5>A DTO should carry data, not perform database queries, register students, or send notifications.</h5><h5>Those responsibilities belong in services or other appropriate application components.</h5><h4>&#128073; Validate data at the appropriate boundary</h4><p>Typed properties do not automatically validate untrusted input. Use Laravel validation or suitable domain-level validation where required.</p><h4>&#128073; Use immutable DTOs where appropriate</h4><p>PHP&#8217;s <code>readonly</code> classes can prevent accidental reassignment of properties after initialization.</p><h4>&#128073; Avoid coupling services to HTTP requests</h4><p>Prefer passing the data a service actually needs rather than the entire request object.</p><h4>&#128073; Don&#8217;t introduce DTOs unnecessarily</h4><p>For a small CRUD operation, a validated array may be enough. Introduce a DTO when its explicit structure and separation of concerns provide real value.</p><div><hr></div><h2>&#128683; Common DTO Mistakes to Avoid</h2><p><strong>Mistake 1: Assuming a DTO validates data automatically</strong></p><p>A plain PHP DTO does not enforce Laravel validation rules.</p><p><strong>Mistake 2: Treating DTOs as Eloquent models</strong></p><p>DTOs carry data; Eloquent models handle database persistence and ORM functionality.</p><p><strong>Mistake 3: Putting business logic inside DTOs</strong></p><p>Keep the data structure separate from registration workflows, database operations, and external integrations.</p><p><strong>Mistake 4: Passing every request field into a DTO</strong></p><p>Map only the fields required for the operation. Use validated input and deliberate field selection.</p><p><strong>Mistake 5: Creating DTOs for every method</strong></p><p>A DTO should solve a real design problem rather than add unnecessary classes and mapping code.</p><div><hr></div><h2>&#127937; Conclusion</h2><p>DTOs are a practical design technique for building maintainable Laravel applications.</p><p>They provide a structured way to transfer data between controllers, services, queued jobs, and other application components. By making data contracts explicit, DTOs can improve readability, reduce coupling, and simplify testing.</p><p>In our student registration example, we used a DTO to separate validated registration data from the HTTP request and the database operation.</p><p>We also explored Spatie Laravel Data, which can reduce repetitive code and provide additional functionality for validation, transformation, and serialization.</p><p>Start with plain PHP DTOs to understand the underlying concept. Introduce them where your application benefits from explicit data contracts, and consider Spatie Laravel Data when your requirements become more complex.</p><p>Remember these three principles:</p><ul><li><p><strong>Validation</strong> determines whether incoming data is acceptable.</p></li><li><p><strong>DTOs</strong> define the structure of the data being transferred.</p></li><li><p><strong>Services</strong> coordinate the business operation.</p></li></ul><p>Used appropriately, DTOs can help your Laravel applications remain easier to understand, test, and maintain as they grow.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 SOLID Principles: Write Cleaner, Scalable, and Maintainable Code]]></title><description><![CDATA[How to design applications that are easier to change, test, extend, and scale]]></description><link>https://newsletter.priteshbhanushali.com/p/solid-principles-write-cleaner-scalable</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/solid-principles-write-cleaner-scalable</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Wed, 28 Oct 2026 05:30:43 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/7f98cedf-2aa3-432e-80b1-410dceadeff6_1310x1201.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Writing code that works is only the beginning.</p><p>As an application grows, the difficult part isn&#8217;t adding another feature. The difficult part is adding that feature <strong>without breaking the existing system</strong>.</p><p>A payment provider changes.</p><p>A new notification channel is introduced.</p><p>A business rule becomes more complicated.</p><p>A report needs another output format.</p><p>A third-party API is replaced.</p><p>Suddenly, a class that started as 100 lines has become 800 lines, filled with <code>if/else</code> statements, external API calls, database queries, and business rules.</p><p>This is where <strong>SOLID principles</strong> become useful.</p><p>SOLID is a collection of five object-oriented design principles:</p><ul><li><p><strong>S - Single Responsibility Principle</strong></p></li><li><p><strong>O - Open/Closed Principle</strong></p></li><li><p><strong>L - Liskov Substitution Principle</strong></p></li><li><p><strong>I - Interface Segregation Principle</strong></p></li><li><p><strong>D - Dependency Inversion Principle</strong></p></li></ul><p>But there is an important misconception:</p><blockquote><p><strong>SOLID is not about creating more classes. It&#8217;s about making change safer and cheaper.</strong></p></blockquote><p>In this article, we&#8217;ll explore each principle using realistic Laravel and PHP examples.</p><div><hr></div><h1>&#127919; What Problem Does SOLID Actually Solve?</h1><p>Imagine an order-processing system.</p><p>Initially, you have:</p><pre><code><code>OrderController
      |
      v
OrderService
      |
      +-- Validate order
      +-- Calculate price
      +-- Charge payment
      +-- Generate invoice
      +-- Send email
      +-- Send SMS
      +-- Write audit log</code></code></pre><p>This might work perfectly when the application is small.</p><p>But eventually:</p><ul><li><p>Stripe becomes PayPal</p></li><li><p>Email becomes WhatsApp</p></li><li><p>Invoice generation changes</p></li><li><p>Pricing rules become more complicated</p></li><li><p>Multiple payment gateways are required</p></li><li><p>Different tenants need different providers</p></li></ul><p>Now every change requires modifying the same large class.</p><p>SOLID helps us move toward:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9NGw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9NGw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!9NGw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!9NGw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!9NGw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!9NGw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:661026,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/222312321?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9NGw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!9NGw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!9NGw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!9NGw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cbe2514-24a7-4e42-8569-5a4b608a06d4_1024x572.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each component has a clear responsibility and dependencies can be replaced without rewriting the business logic.</p><div><hr></div><h1>1. S - Single Responsibility Principle</h1><blockquote><p><strong>A class should have one reason to change.</strong></p></blockquote><p>SRP doesn&#8217;t mean a class can have only one method.</p><p>It means a class should have <strong>one cohesive responsibility</strong>.</p><p>If a class handles payment, invoices, notifications, validation, and reporting, it has too many reasons to change.</p><h2><strong>&#128073; </strong>Before: One class doing everything</h2><p>Consider an order-processing service:</p><pre><code>class OrderService
{
    public function process(Order $order): void
    {
        // Validate order
        if ($order-&gt;items-&gt;isEmpty()) {
            throw new Exception('Order has no items.');
        }

        // Calculate total
        $total = $order-&gt;items-&gt;sum(
            fn ($item) =&gt; $item-&gt;price * $item-&gt;quantity
        );

        // Charge payment
        $payment = Http::post(
            'https://payment.example.com/charge',
            [
                'amount' =&gt; $total,
                'currency' =&gt; 'USD',
                'customer_id' =&gt; $order-&gt;customer_id,
            ]
        );

        if (!$payment-&gt;successful()) {
            throw new Exception('Payment failed.');
        }

        // Create invoice
        Invoice::create([
            'order_id' =&gt; $order-&gt;id,
            'amount' =&gt; $total,
        ]);

        // Send email
        Mail::to($order-&gt;customer-&gt;email)
            -&gt;send(new OrderConfirmationMail($order));

        // Write audit log
        Log::info('Order processed', [
            'order_id' =&gt; $order-&gt;id,
            'amount' =&gt; $total,
        ]);
    }
}</code></pre><p>This class is responsible for:</p><ul><li><p>validation</p></li><li><p>pricing</p></li><li><p>payment</p></li><li><p>invoice generation</p></li><li><p>email</p></li><li><p>logging</p></li></ul><p>That&#8217;s too much.</p><h2>Why is this a problem?</h2><p>Suppose the payment provider changes.</p><p>You modify <code>OrderService</code>.</p><p>Suppose invoice generation changes.</p><p>You modify <code>OrderService</code> again.</p><p>Suppose the notification system changes.</p><p>Again, <code>OrderService</code>.</p><p>The class becomes a <strong>change hotspot</strong>.</p><p>The bigger it becomes, the higher the risk that a small change breaks unrelated functionality.</p><h1><strong>&#128073; </strong>After: Separate responsibilities</h1><p>Extract cohesive responsibilities.</p><h3>Order validator</h3><pre><code>class OrderValidator
{
    public function validate(Order $order): void
    {
        if ($order-&gt;items-&gt;isEmpty()) {
            throw new Exception('Order has no items.');
        }
    }
}</code></pre><h3>Price calculator</h3><pre><code><code>class OrderTotalCalculator
{
    public function calculate(Order $order): float
    {
        return $order-&gt;items-&gt;sum(
            fn ($item) =&gt; $item-&gt;price * $item-&gt;quantity
        );
    }
}</code></code></pre><h3>Invoice service</h3><pre><code><code>class InvoiceService
{
    public function create(
        Order $order,
        float $amount
    ): Invoice {
        return Invoice::create([
            'order_id' =&gt; $order-&gt;id,
            'amount' =&gt; $amount,
        ]);
    }
}</code></code></pre><h3>Notification service</h3><pre><code><code>class OrderNotificationService
{
    public function sendConfirmation(
        Order $order
    ): void {
        Mail::to($order-&gt;customer-&gt;email)
            -&gt;send(new OrderConfirmationMail($order));
    }
}</code></code></pre><p>Now <code>OrderService</code> coordinates the workflow:</p><pre><code><code>class OrderService
{
    public function __construct(
        private OrderValidator $validator,
        private OrderTotalCalculator $calculator,
        private PaymentService $paymentService,
        private InvoiceService $invoiceService,
        private OrderNotificationService $notificationService,
    ) {}

    public function process(Order $order): void
    {
        $this-&gt;validator-&gt;validate($order);

        $total = $this-&gt;calculator-&gt;calculate($order);

        $this-&gt;paymentService-&gt;charge(
            $order,
            $total
        );

        $this-&gt;invoiceService-&gt;create(
            $order,
            $total
        );

        $this-&gt;notificationService
            -&gt;sendConfirmation($order);
    }
}</code></code></pre><p>Now each class has a clearer responsibility.</p><h3>A practical SRP rule</h3><p>When reviewing a class, ask:</p><blockquote><p><strong>&#8220;How many different reasons could cause this class to change?&#8221;</strong></p></blockquote><p>If the answer is five, the class probably has five responsibilities.</p><div><hr></div><h1>2. O - Open/Closed Principle</h1><blockquote><p><strong>Software should be open for extension but closed for modification.</strong></p></blockquote><p>This means we should be able to introduce new behavior without repeatedly modifying stable business logic.</p><p>Payment processing is a good example.</p><p>Imagine your application supports multiple payment providers.</p><h2><strong>&#128073; </strong>Before: Growing conditional logic</h2><pre><code><code>class PaymentService
{
    public function charge(
        string $gateway,
        float $amount
    ): void {
        if ($gateway === 'stripe') {

            Http::post(
                'https://stripe.example.com/charge',
                [
                    'amount' =&gt; $amount,
                ]
            );

        } elseif ($gateway === 'paypal') {

            Http::post(
                'https://paypal.example.com/payment',
                [
                    'amount' =&gt; $amount,
                ]
            );

        } elseif ($gateway === 'razorpay') {

            Http::post(
                'https://razorpay.example.com/payment',
                [
                    'amount' =&gt; $amount,
                ]
            );
        }
    }
}</code></code></pre><p>Today you have three providers.</p><p>Tomorrow:</p><pre><code><code>Stripe
PayPal
Razorpay
Adyen
Authorize.Net
Apple Pay
Google Pay
...</code></code></pre><p>The <code>if/else</code> chain keeps growing.</p><p>Every new provider requires modifying <code>PaymentService</code>.</p><p>That&#8217;s exactly the kind of change OCP tries to avoid.</p><h1><strong>&#128073; </strong>After: Use an abstraction</h1><p>Define a common contract:</p><pre><code><code>interface PaymentGateway
{
    public function charge(
        float $amount
    ): PaymentResult;
}</code></code></pre><p>Stripe:</p><pre><code><code>class StripeGateway implements PaymentGateway
{
    public function charge(
        float $amount
    ): PaymentResult {
        // Stripe-specific implementation

        return PaymentResult::success();
    }
}</code></code></pre><p>PayPal:</p><pre><code><code>class PayPalGateway implements PaymentGateway
{
    public function charge(
        float $amount
    ): PaymentResult {
        // PayPal-specific implementation

        return PaymentResult::success();
    }
}</code></code></pre><p>Now the service depends on the abstraction:</p><pre><code><code>class PaymentService
{
    public function __construct(
        private PaymentGateway $gateway
    ) {}

    public function charge(
        float $amount
    ): PaymentResult {
        return $this-&gt;gateway-&gt;charge($amount);
    }
}</code></code></pre><p>Adding another provider doesn&#8217;t require modifying <code>PaymentService</code>.</p><pre><code><code>class AdyenGateway implements PaymentGateway
{
    public function charge(
        float $amount
    ): PaymentResult {
        // Adyen implementation
    }
}</code></code></pre><p>That&#8217;s the practical idea behind OCP.</p><h2>Laravel makes this easier</h2><p>Laravel&#8217;s service container can bind the interface:</p><pre><code><code>$this-&gt;app-&gt;bind(
    PaymentGateway::class,
    StripeGateway::class
);</code></code></pre><p>Now Laravel can inject the correct implementation automatically.</p><div><hr></div><h1>3. L - Liskov Substitution Principle</h1><blockquote><p><strong>If a class claims to implement an abstraction, it should be safely usable wherever that abstraction is expected.</strong></p></blockquote><p>LSP is essentially about <strong>behavioral compatibility</strong>.</p><p>Inheritance or interface implementation isn&#8217;t enough.</p><p>The implementation must actually honor the contract.</p><p>Consider a reporting system.</p><h2><strong>&#128073; </strong>Before: Incorrect abstraction</h2><pre><code><code>abstract class ReportGenerator
{
    abstract public function generate(
        array $data
    ): string;

    public function exportToPdf(
        string $content
    ): string {
        return "PDF: " . $content;
    }
}</code></code></pre><p>Now we create a CSV report:</p><pre><code>class CsvReportGenerator extends ReportGenerator
{
    public function generate(
        array $data
    ): string {
        return 'CSV report';
    }

    public function exportToPdf(
        string $content
    ): string {
        throw new Exception(
            'CSV cannot be exported to PDF.'
        );
    }
}</code></pre><p>Now this code is dangerous:</p><pre><code><code>function generateReport(
    ReportGenerator $report
): string {
    $content = $report-&gt;generate([]);

    return $report-&gt;exportToPdf($content);
}</code></code></pre><p>A caller expects <code>ReportGenerator</code> to support PDF export.</p><p>But <code>CsvReportGenerator</code> doesn&#8217;t.</p><p>The subclass cannot safely substitute the parent abstraction.</p><p>That&#8217;s an LSP violation.</p><h1><strong>&#128073; </strong>After: Model capabilities correctly</h1><p>Separate report generation from PDF export.</p><pre><code><code>interface ReportGenerator
{
    public function generate(
        array $data
    ): string;
}</code></code></pre><pre><code><code>interface PdfExporter
{
    public function exportToPdf(
        string $content
    ): string;
}</code></code></pre><p>A PDF-capable report can implement both:</p><pre><code><code>class SalesReportGenerator
    implements ReportGenerator, PdfExporter
{
    public function generate(
        array $data
    ): string {
        return 'Sales report';
    }

    public function exportToPdf(
        string $content
    ): string {
        return "PDF: " . $content;
    }
}</code></code></pre><p>A CSV report only implements what it actually supports:</p><pre><code><code>class CustomerCsvReportGenerator
    implements ReportGenerator
{
    public function generate(
        array $data
    ): string {
        return 'Customer CSV report';
    }
}</code></code></pre><p>Now the abstraction accurately represents the capabilities of the implementation.</p><h3>A useful LSP smell</h3><p>If an implementation frequently contains:</p><pre><code><code>throw new Exception('Not supported');</code></code></pre><p>for methods it inherited or implemented, stop and reconsider the abstraction.</p><p>You may have an LSP or interface-design problem.</p><h1><strong>&#128073; </strong>Composition Over Inheritance</h1><p>LSP also leads to an important architectural lesson:</p><blockquote><p><strong>Prefer composition when behavior needs to vary independently.</strong></p></blockquote><p>Instead of building complicated inheritance trees:</p><pre><code><code>Payment
   |
   +-- OnlinePayment
         |
         +-- CreditCardPayment
                |
                +-- StripeCreditCardPayment</code></code></pre><p>you can compose behavior:</p><pre><code><code>PaymentService
      |
      +-- PaymentGateway
      +-- PaymentValidator
      +-- PaymentLogger</code></code></pre><p>Composition often makes business systems easier to evolve because behavior can be replaced independently.</p><div><hr></div><h1>4. I - Interface Segregation Principle</h1><blockquote><p><strong>Clients should not be forced to depend on methods they don&#8217;t use.</strong></p></blockquote><p>In other words:</p><blockquote><p><strong>Prefer small, focused interfaces over large, &#8220;do everything&#8221; interfaces.</strong></p></blockquote><p>Consider a notification system.</p><h2><strong>&#128073; </strong>Before: A fat interface</h2><pre><code><code>interface NotificationService
{
    public function sendEmail(
        string $to,
        string $message
    ): void;

    public function sendSms(
        string $phone,
        string $message
    ): void;

    public function sendPush(
        string $deviceToken,
        string $message
    ): void;

    public function sendSlack(
        string $channel,
        string $message
    ): void;
}</code></code></pre><p>Now an SMS implementation must implement everything:</p><pre><code><code>class SmsNotification implements NotificationService
{
    public function sendEmail(
        string $to,
        string $message
    ): void {
        throw new Exception('Not supported');
    }

    public function sendSms(
        string $phone,
        string $message
    ): void {
        // Send SMS
    }

    public function sendPush(
        string $deviceToken,
        string $message
    ): void {
        throw new Exception('Not supported');
    }

    public function sendSlack(
        string $channel,
        string $message
    ): void {
        throw new Exception('Not supported');
    }
}</code></code></pre><p>This interface is too large.</p><div><hr></div><h1><strong>&#128073; </strong>After: Smaller interfaces</h1><pre><code><code>interface EmailNotifier
{
    public function sendEmail(
        string $to,
        string $message
    ): void;
}</code></code></pre><pre><code><code>interface SmsNotifier
{
    public function sendSms(
        string $phone,
        string $message
    ): void;
}</code></code></pre><pre><code><code>interface PushNotifier
{
    public function sendPush(
        string $deviceToken,
        string $message
    ): void;
}</code></code></pre><p>Now an SMS provider only needs to implement:</p><pre><code><code>class TwilioSmsNotifier implements SmsNotifier
{
    public function sendSms(
        string $phone,
        string $message
    ): void {
        // Send SMS
    }
}</code></code></pre><p>This reduces coupling and makes implementations easier to understand.</p><h3>A practical question</h3><p>Don&#8217;t ask:</p><blockquote><p>&#8220;How can I create one interface for everything?&#8221;</p></blockquote><p>Ask:</p><blockquote><p><strong>&#8220;What capability does this consumer actually need?&#8221;</strong></p></blockquote><div><hr></div><h1>5. D - Dependency Inversion Principle</h1><blockquote><p><strong>High-level business logic should not depend directly on low-level implementation details. Both should depend on abstractions.</strong></p></blockquote><p>This is one of the most valuable SOLID principles for Laravel applications.</p><p>Consider an order service that directly creates a Stripe client.</p><h2><strong>&#128073; </strong>Before: Business logic coupled to Stripe</h2><pre><code><code>class OrderService
{
    public function process(Order $order): void
    {
        $stripe = new \Stripe\StripeClient(
            config('services.stripe.secret')
        );

        $stripe-&gt;paymentIntents-&gt;create([
            'amount' =&gt; $order-&gt;total * 100,
            'currency' =&gt; 'usd',
        ]);

        $order-&gt;update([
            'status' =&gt; 'paid',
        ]);
    }
}</code></code></pre><p>Now <code>OrderService</code> knows:</p><ul><li><p>Stripe exists</p></li><li><p>Stripe&#8217;s SDK exists</p></li><li><p>how Stripe authentication works</p></li><li><p>how Stripe represents payments</p></li></ul><p>If the business moves from Stripe to another provider, the business service must change.</p><div><hr></div><h1><strong>&#128073; </strong>After: Depend on an abstraction</h1><p>Create a contract:</p><pre><code><code>interface PaymentGateway
{
    public function charge(
        float $amount,
        string $currency
    ): PaymentResult;
}</code></code></pre><p>Stripe becomes an implementation detail:</p><pre><code><code>class StripePaymentGateway
    implements PaymentGateway
{
    public function __construct(
        private \Stripe\StripeClient $stripe
    ) {}

    public function charge(
        float $amount,
        string $currency
    ): PaymentResult {
        $payment = $this-&gt;stripe
            -&gt;paymentIntents
            -&gt;create([
                'amount' =&gt; (int) ($amount * 100),
                'currency' =&gt; $currency,
            ]);

        return PaymentResult::success(
            $payment-&gt;id
        );
    }
}</code></code></pre><p>The business service now depends only on the contract:</p><pre><code><code>class OrderService
{
    public function __construct(
        private PaymentGateway $paymentGateway
    ) {}

    public function process(Order $order): void
    {
        $result = $this-&gt;paymentGateway-&gt;charge(
            $order-&gt;total,
            'USD'
        );

        if (!$result-&gt;successful()) {
            throw new PaymentFailedException();
        }

        $order-&gt;update([
            'status' =&gt; 'paid',
        ]);
    }
}</code></code></pre><p>Laravel connects the abstraction with the implementation:</p><pre><code><code>$this-&gt;app-&gt;bind(
    PaymentGateway::class,
    StripePaymentGateway::class
);</code></code></pre><p>The architecture becomes:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!diJK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!diJK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!diJK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!diJK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!diJK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!diJK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png" width="1024" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:616805,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/222312321?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!diJK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 424w, https://substackcdn.com/image/fetch/$s_!diJK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 848w, https://substackcdn.com/image/fetch/$s_!diJK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 1272w, https://substackcdn.com/image/fetch/$s_!diJK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c29b682-806e-42da-b67b-b6d9bac5ecd3_1024x572.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The important part:</p><blockquote><p><strong>OrderService doesn&#8217;t depend on Stripe. It depends on the concept of a payment gateway.</strong></p></blockquote><h1><strong>&#9888;&#65039; </strong>Code Smells That Often Point Toward SOLID</h1><p>You don&#8217;t need to memorize every SOLID definition.</p><p>Instead, learn to recognize the symptoms.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9Bvc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9Bvc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!9Bvc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!9Bvc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!9Bvc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!9Bvc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1561849,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/222312321?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9Bvc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!9Bvc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!9Bvc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!9Bvc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F42e29d29-e2ed-4c89-b184-f1fa8c8b4d55_1536x1024.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h1><strong>&#128721; </strong>When NOT to Over-Engineer</h1><p>Before introducing an abstraction, ask:</p><h3>1. Is this behavior actually expected to vary?</h3><p>If not, keep it simple.</p><h3>2. Is there a real architectural boundary?</h3><p>External payment providers, messaging providers, storage systems, and third-party APIs often justify abstractions.</p><h3>3. Does the abstraction make testing easier?</h3><p>If yes, that&#8217;s a strong reason.</p><h3>4. Is the code difficult to change today?</h3><p>SOLID is especially valuable around areas that change frequently.</p><h3>5. Are you solving a real problem?</h3><p>Don&#8217;t build an elaborate architecture for a hypothetical requirement that may never happen.</p><div><hr></div><h1><strong>&#10024; </strong>Final Takeaway</h1><p>SOLID isn&#8217;t a checklist you apply mechanically.</p><p>It&#8217;s a set of <strong>design heuristics for managing change</strong>.</p><p>The goal isn&#8217;t to create the most abstract architecture possible.</p><p>The goal is to create software where:</p><pre><code><code>Business Logic
      &#8595;
Stable Abstractions
      &#8595;
Replaceable Implementations</code></code></pre><p>When a payment provider changes, you shouldn&#8217;t need to rewrite order processing.</p><p>When a notification channel changes, you shouldn&#8217;t need to rewrite business logic.</p><p>When a new report format is introduced, existing report generation shouldn&#8217;t break.</p><p>And when you write tests, you shouldn&#8217;t need to call every external service.</p><p>That&#8217;s where SOLID provides real value.</p><blockquote><p><strong>Good architecture isn&#8217;t about writing more code.</strong></p><p><strong>It&#8217;s about making change cheaper.</strong></p></blockquote><p>And that&#8217;s ultimately what SOLID is about.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[CDN: The Secret Behind Fast and Scalable Web Applications]]></title><description><![CDATA[How Content Delivery Networks reduce latency, handle massive traffic, and bring your application closer to users]]></description><link>https://newsletter.priteshbhanushali.com/p/cdn-the-secret-behind-fast-and-scalable</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/cdn-the-secret-behind-fast-and-scalable</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Thu, 08 Oct 2026 06:30:36 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a65e049e-f20a-45df-97f3-2637a3ef1609_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you have built a web application hosted on a server in Mumbai.</p><p>Initially, everything works perfectly.</p><p>Then your application starts getting users from:</p><ul><li><p>Mumbai</p></li><li><p>London</p></li><li><p>New York</p></li><li><p>Singapore</p></li><li><p>Sydney</p></li></ul><p>Now every user request has to travel back to your origin server.</p><p>As traffic increases, your server starts experiencing:</p><ul><li><p>Higher latency</p></li><li><p>Increased bandwidth consumption</p></li><li><p>More CPU and network pressure</p></li><li><p>Slower response times</p></li><li><p>Increased infrastructure costs</p></li><li><p>Potential downtime</p></li></ul><p>So, how do large-scale applications deliver content quickly to users around the world?</p><p>The answer is <strong>CDN - Content Delivery Network</strong>.</p><h2>&#128483;&#65039; What Is a CDN?</h2><p>A <strong>Content Delivery Network is a geographically distributed network of servers that delivers content from locations closer to users.</strong></p><p>Instead of every user requesting content directly from your origin server:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qAI-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qAI-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 424w, https://substackcdn.com/image/fetch/$s_!qAI-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 848w, https://substackcdn.com/image/fetch/$s_!qAI-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 1272w, https://substackcdn.com/image/fetch/$s_!qAI-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!qAI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png" width="1456" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1968409,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/219111275?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!qAI-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 424w, https://substackcdn.com/image/fetch/$s_!qAI-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 848w, https://substackcdn.com/image/fetch/$s_!qAI-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 1272w, https://substackcdn.com/image/fetch/$s_!qAI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F174815b5-2d03-4801-a276-9f800f90afe3_1990x842.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>a CDN places edge servers between users and your origin:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UUi5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UUi5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 424w, https://substackcdn.com/image/fetch/$s_!UUi5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 848w, https://substackcdn.com/image/fetch/$s_!UUi5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 1272w, https://substackcdn.com/image/fetch/$s_!UUi5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!UUi5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png" width="1456" height="915" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:915,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2877615,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/219111275?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UUi5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 424w, https://substackcdn.com/image/fetch/$s_!UUi5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 848w, https://substackcdn.com/image/fetch/$s_!UUi5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 1272w, https://substackcdn.com/image/fetch/$s_!UUi5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdfd0197e-715e-4925-b468-a4ab90ba02e2_1990x1250.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The CDN stores frequently requested content at these <strong>edge locations</strong>.</p><p>When a user requests content, the CDN tries to serve it from the closest suitable edge location.</p><p>The core idea is simple:</p><blockquote><p><strong>Bring content closer to the user.</strong></p></blockquote><h1>&#129300; Why Do We Need a CDN?</h1><p>Let&#8217;s take a simple example.</p><p>Your application is hosted in Mumbai.</p><p>A user in Mumbai might have relatively low network latency:</p><pre><code><code>Mumbai User
     |
     | Shorter distance
     v
Mumbai Server</code></code></pre><p>But a user in New York has to communicate with infrastructure thousands of kilometers away:</p><pre><code><code>New York User
     |
     | Long network distance
     v
Mumbai Server</code></code></pre><p>Now imagine millions of requests.</p><p>Your origin server has to repeatedly send the same files:</p><pre><code><code>app.js
style.css
logo.png
product-image.jpg
font.woff2
video.mp4</code></code></pre><p>Most of these files don&#8217;t change frequently.</p><p>So instead of making the origin serve the same content repeatedly, a CDN can cache that content closer to users.</p><h1>&#129302; How Does a CDN Work?</h1><p>Let&#8217;s take an image:</p><pre><code><code>https://example.com/images/product-101.jpg</code></code></pre><p>A user requests the image.</p><p>The request reaches the CDN.</p><p>The CDN checks:</p><blockquote><p>&#8220;Do I already have this file in my cache?&#8221;</p></blockquote><p>There are two possibilities.</p><h2>&#128994; Cache Hit</h2><p>The content already exists at the edge.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZlY4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZlY4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 424w, https://substackcdn.com/image/fetch/$s_!ZlY4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 848w, https://substackcdn.com/image/fetch/$s_!ZlY4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 1272w, https://substackcdn.com/image/fetch/$s_!ZlY4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZlY4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png" width="1456" height="1210" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1210,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2118590,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/219111275?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZlY4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 424w, https://substackcdn.com/image/fetch/$s_!ZlY4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 848w, https://substackcdn.com/image/fetch/$s_!ZlY4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 1272w, https://substackcdn.com/image/fetch/$s_!ZlY4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc28aae4d-0735-416d-ae74-af7389f4df4d_1814x1508.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The CDN immediately returns the image.</p><p>The origin isn&#8217;t contacted.</p><h2>&#128308; Cache Miss</h2><p>The content doesn&#8217;t exist at the edge.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7JlL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7JlL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 424w, https://substackcdn.com/image/fetch/$s_!7JlL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 848w, https://substackcdn.com/image/fetch/$s_!7JlL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 1272w, https://substackcdn.com/image/fetch/$s_!7JlL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7JlL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png" width="1012" height="1528" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1528,&quot;width&quot;:1012,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1713858,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/219111275?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7JlL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 424w, https://substackcdn.com/image/fetch/$s_!7JlL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 848w, https://substackcdn.com/image/fetch/$s_!7JlL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 1272w, https://substackcdn.com/image/fetch/$s_!7JlL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa2368a3-0e60-4619-a16a-f166b23a02e2_1012x1528.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The CDN retrieves the content from the origin.</p><p>It can then store the content in its cache.</p><p>The next user requesting the same content can receive it directly from the CDN.</p><h1>&#128161;Understanding Cache Hit and Cache Miss</h1><p>This is one of the most important concepts when working with CDNs.</p><p>Suppose 10,000 users request:</p><pre><code><code>/logo.png</code></code></pre><p>Without caching:</p><pre><code><code>10,000 Requests
       |
       v
    Origin</code></code></pre><p>With CDN caching:</p><pre><code><code>10,000 Requests
       |
       v
      CDN
       |
       +---- 9,999 Cache Hits
       |
       +---- 1 Cache Miss
                 |
                 v
               Origin</code></code></pre><p>The exact number of origin requests depends on timing, cache behavior, configuration, and revalidation, but the important idea is that <strong>repeated requests can be served from the edge</strong>.</p><h1>&#128190; What Does a CDN Cache?</h1><p>CDNs are commonly used for content that is requested frequently and doesn&#8217;t change every second.</p><p>Typical examples include:</p><h3>Images</h3><pre><code><code>.jpg
.png
.webp
.svg</code></code></pre><h3>JavaScript</h3><pre><code><code>app.js
vendor.js
main.js</code></code></pre><h3>CSS</h3><pre><code><code>app.css
style.css</code></code></pre><h3>Fonts</h3><pre><code><code>.woff
.woff2
.ttf</code></code></pre><h3>Videos</h3><pre><code><code>.mp4
.webm</code></code></pre><h3>Documents</h3><pre><code><code>.pdf
.zip</code></code></pre><h3>Static HTML</h3><p>Depending on the application architecture, HTML pages can also be cached.</p><h1>&#127760; CDN Architecture</h1><p>A simplified CDN architecture looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2zkT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2zkT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 424w, https://substackcdn.com/image/fetch/$s_!2zkT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 848w, https://substackcdn.com/image/fetch/$s_!2zkT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 1272w, https://substackcdn.com/image/fetch/$s_!2zkT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!2zkT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png" width="1456" height="1097" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1097,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2952215,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/219111275?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2zkT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 424w, https://substackcdn.com/image/fetch/$s_!2zkT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 848w, https://substackcdn.com/image/fetch/$s_!2zkT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 1272w, https://substackcdn.com/image/fetch/$s_!2zkT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffc95147-7cda-4c6d-8a71-70319bdc76d8_2028x1528.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <strong>origin</strong> contains the original content.</p><p>The <strong>edge servers</strong> cache frequently requested content.</p><p>The user ideally receives the content from an edge location instead of going all the way back to the origin every time.</p><h1>&#129513; What Is an Origin Server?</h1><p>The <strong>origin</strong> is the original source of the content.</p><p>For example, your origin could be:</p><pre><code><code>Application Server</code></code></pre><p>or:</p><pre><code><code>Object Storage</code></code></pre><p>or:</p><pre><code><code>A dedicated media server</code></code></pre><p>For a web application, you might have:</p><pre><code><code>Origin
   |
   +&#9472;&#9472; HTML
   +&#9472;&#9472; JavaScript
   +&#9472;&#9472; CSS
   +&#9472;&#9472; Images
   +&#9472;&#9472; Videos</code></code></pre><p>The CDN retrieves content from the origin when it doesn&#8217;t already have a valid cached copy.</p><h1>&#128200; CDN Caching Strategy</h1><p>Caching is where CDN architecture becomes interesting.</p><p>You need to decide:</p><blockquote><p><strong>What should be cached?</strong></p><p><strong>How long should it be cached?</strong></p><p><strong>When should it expire?</strong></p><p><strong>How should updated content reach users?</strong></p></blockquote><p>These decisions are usually controlled through HTTP caching headers and CDN configuration.</p><p>For example:</p><pre><code><code>Cache-Control: public, max-age=3600</code></code></pre><p>This means the response can be cached and considered fresh for 3600 seconds.</p><h1>Cache-Control</h1><p>Some commonly used directives are:</p><h3>public</h3><pre><code><code>Cache-Control: public</code></code></pre><p>Allows shared caches to store the response.</p><h3>max-age</h3><pre><code><code>Cache-Control: max-age=3600</code></code></pre><p>Specifies how long the response can be considered fresh.</p><h3>no-store</h3><pre><code><code>Cache-Control: no-store</code></code></pre><p>Tells caches not to store the response.</p><h3>immutable</h3><pre><code><code>Cache-Control: public, max-age=31536000, immutable</code></code></pre><p>This can be useful for versioned static assets that never change at the same URL.</p><h1>&#10067;The Cache Invalidation Problem</h1><p>Caching gives us performance.</p><p>But caching introduces another problem:</p><blockquote><p><strong>What happens when the original content changes?</strong></p></blockquote><p>Suppose your CDN cached:</p><pre><code><code>logo-v1.png</code></code></pre><p>You update the logo.</p><p>But users still receive the cached version.</p><p>Why?</p><p>Because the CDN still considers the cached object valid.</p><p>There are several ways to solve this.</p><h2>Strategy 1: TTL</h2><p>Set an expiration time.</p><pre><code><code>Cache-Control: max-age=3600</code></code></pre><p>After the specified period, the cached object can become stale and the CDN may retrieve a newer version.</p><h2>Strategy 2: Cache Purging</h2><p>When you deploy new content:</p><pre><code><code>Deploy
  |
  v
Purge CDN Cache
  |
  v
New Content</code></code></pre><p>The CDN removes the selected cached objects.</p><h2>Strategy 3: Versioned Assets</h2><p>Instead of:</p><pre><code><code>app.js</code></code></pre><p>use:</p><pre><code><code>app.v1.js</code></code></pre><p>After deployment:</p><pre><code><code>app.v2.js</code></code></pre><p>The URL changes, so the CDN treats it as a new resource.</p><p>This is a very common strategy for JavaScript, CSS, images, and other static assets.</p><h1>&#128680; Common CDN Mistakes</h1><h3>1. Caching Everything</h3><p>Not every response should be cached.</p><h3>2. Ignoring Cache Invalidation</h3><p>Users may continue receiving stale content.</p><h3>3. Using Long TTLs Without Versioning</h3><p>Content updates can take too long to propagate.</p><h3>4. Caching Personalized Data</h3><p>This can create serious security and privacy issues.</p><h3>5. Ignoring Cache Keys</h3><p>Different requests can require different cached responses.</p><h3>6. Not Monitoring Cache Performance</h3><p>Without metrics, you don&#8217;t know whether your CDN configuration is effective.</p><h1>&#127970; Popular CDN Providers</h1><p>Some widely used CDN platforms include:</p><ul><li><p>Cloudflare</p></li><li><p>Amazon CloudFront</p></li><li><p>Akamai</p></li><li><p>Fastly</p></li><li><p>Google Cloud CDN</p></li></ul><p>The right choice depends on your geographic requirements, traffic volume, caching needs, security requirements, cloud environment, and cost.</p><h1>&#11088; CDN Best Practices</h1><p>If you&#8217;re designing a CDN strategy for production, keep these principles in mind:</p><h3>1. Cache What Changes Slowly</h3><p>Good candidates include:</p><pre><code><code>Images
CSS
JavaScript
Fonts
Videos
Documents</code></code></pre><h3>2. Use Long TTLs for Versioned Assets</h3><p>For example:</p><pre><code><code>app.a81f92.js</code></code></pre><p>can have a long cache lifetime because the filename changes when the content changes.</p><h3>3. Keep Cache Keys Simple</h3><p>Only include request attributes that actually change the response.</p><h3>4. Protect Sensitive Data</h3><p>Never make private or personalized content publicly cacheable by accident.</p><h3>5. Plan Cache Invalidation</h3><p>Have a clear strategy for handling content updates.</p><h3>6. Monitor Cache Hit Ratio</h3><p>Your CDN configuration should be driven by real traffic data.</p><h3>7. Test Globally</h3><p>Test performance from different geographic regions, not just from your development machine.</p><h1>&#127919; Final Takeaway</h1><p>If you remember only five things about CDN, remember these:</p><p><strong>1. CDN reduces latency</strong><br>Content is delivered from locations closer to users.</p><p><strong>2. CDN reduces origin traffic</strong><br>Repeated requests can be served from the edge.</p><p><strong>3. Cache strategy is critical</strong><br>TTL, cache keys, and invalidation determine how effectively your CDN works.</p><p><strong>4. Not everything should be cached</strong><br>Personalized and sensitive content requires special care.</p><p><strong>5. Measure the results</strong><br>Cache hit ratio, latency, origin traffic, and geographic performance tell you whether your CDN strategy is working.</p><blockquote><p><strong>A CDN doesn&#8217;t make your content disappear from the origin. It makes your content available closer to the people who need it.</strong></p></blockquote><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚦Load Balancer: The Traffic Controller of Modern Distributed Systems]]></title><description><![CDATA[How load balancing distributes traffic, improves availability, and keeps applications scalable]]></description><link>https://newsletter.priteshbhanushali.com/p/load-balancer-the-traffic-controller</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/load-balancer-the-traffic-controller</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Mon, 05 Oct 2026 05:31:53 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e76e79d9-83aa-4c43-92ec-7e1ff2a7c39a_1310x1201.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine your application is running on a single server.</p><p>At first, everything works perfectly.</p><p>Then your application becomes popular.</p><p>Suddenly, thousands of users are sending requests simultaneously.</p><p>Your server starts experiencing:</p><ul><li><p>High CPU usage</p></li><li><p>Increased response time</p></li><li><p>Memory pressure</p></li><li><p>Connection limits</p></li><li><p>Eventually, downtime</p></li></ul><p>The obvious solution is to add more application servers.</p><p>But this creates another problem:</p><p><strong>Who decides which server should handle each request?</strong></p><p>This is where a <strong>Load Balancer</strong> comes in.</p><h2>&#127959;&#65039; What Is a Load Balancer?</h2><p>A <strong>Load Balancer</strong> is a component that sits between clients and backend servers and distributes incoming traffic across multiple servers.</p><p>Instead of:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZwHM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZwHM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 424w, https://substackcdn.com/image/fetch/$s_!ZwHM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 848w, https://substackcdn.com/image/fetch/$s_!ZwHM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 1272w, https://substackcdn.com/image/fetch/$s_!ZwHM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZwHM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png" width="1456" height="833" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:833,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:83062,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZwHM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 424w, https://substackcdn.com/image/fetch/$s_!ZwHM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 848w, https://substackcdn.com/image/fetch/$s_!ZwHM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 1272w, https://substackcdn.com/image/fetch/$s_!ZwHM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8637319-2c27-4b39-a687-b97c51e938b8_1663x951.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>we introduce a load balancer:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!prL6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!prL6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 424w, https://substackcdn.com/image/fetch/$s_!prL6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 848w, https://substackcdn.com/image/fetch/$s_!prL6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 1272w, https://substackcdn.com/image/fetch/$s_!prL6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!prL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png" width="1456" height="830" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:830,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:170306,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!prL6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 424w, https://substackcdn.com/image/fetch/$s_!prL6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 848w, https://substackcdn.com/image/fetch/$s_!prL6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 1272w, https://substackcdn.com/image/fetch/$s_!prL6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ac288e6-eab6-49c3-8a9e-c6a8b4a846bb_1896x1081.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The client doesn&#8217;t need to know which application server processes the request.</p><p>The load balancer handles that decision.</p><div><hr></div><h1>&#128161;Why Do We Need a Load Balancer?</h1><p>A load balancer primarily helps solve four major problems.</p><h3>Scalability</h3><p>Instead of continuously increasing the capacity of one server, we can add more servers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yiCl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yiCl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 424w, https://substackcdn.com/image/fetch/$s_!yiCl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 848w, https://substackcdn.com/image/fetch/$s_!yiCl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 1272w, https://substackcdn.com/image/fetch/$s_!yiCl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!yiCl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png" width="1456" height="550" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:550,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:44057,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yiCl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 424w, https://substackcdn.com/image/fetch/$s_!yiCl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 848w, https://substackcdn.com/image/fetch/$s_!yiCl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 1272w, https://substackcdn.com/image/fetch/$s_!yiCl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea6e4b22-aaed-4ebd-9df0-888e80fa9b3b_1667x630.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is known as <strong>horizontal scaling</strong>.</p><h3>High Availability</h3><p>What happens if Server B crashes?</p><p>A good load balancer detects that Server B is unhealthy and stops sending traffic to it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8va-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8va-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 424w, https://substackcdn.com/image/fetch/$s_!8va-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 848w, https://substackcdn.com/image/fetch/$s_!8va-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 1272w, https://substackcdn.com/image/fetch/$s_!8va-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!8va-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png" width="1456" height="550" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:550,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:47537,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8va-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 424w, https://substackcdn.com/image/fetch/$s_!8va-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 848w, https://substackcdn.com/image/fetch/$s_!8va-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 1272w, https://substackcdn.com/image/fetch/$s_!8va-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3764fb16-0120-4ea5-9747-22e28ae163fb_1667x630.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Users can continue using the application.</p><h3>Fault Tolerance</h3><p>A failure in one server should not necessarily become a failure of the entire application.</p><p>The load balancer acts as a layer that isolates individual server failures.</p><h3>Performance</h3><p>Traffic can be distributed across multiple servers instead of allowing one server to become overloaded.</p><div><hr></div><h1>&#9878;&#65039; Load Balancing Algorithms</h1><p>Choosing the correct backend server is one of the most important responsibilities of a load balancer.</p><p>There are several common algorithms.</p><h2>Round Robin</h2><p>Requests are distributed sequentially.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aJ41!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aJ41!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!aJ41!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!aJ41!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!aJ41!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!aJ41!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:543009,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aJ41!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!aJ41!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!aJ41!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!aJ41!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5269b8b-8527-4cae-a523-32e18d4ef29e_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Simple and effective when servers have similar capacity.</p><h2>Weighted Round Robin</h2><p>Not all servers need to have equal capacity.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t-op!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t-op!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!t-op!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!t-op!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!t-op!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!t-op!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:581823,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!t-op!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!t-op!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!t-op!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!t-op!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe000cfcc-1aa2-4569-acb8-d1242e562ed5_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Server A receives more traffic.</p><p>This is useful when your infrastructure contains servers with different CPU, memory, or instance sizes.</p><h2>Least Connections</h2><p>The load balancer sends traffic to the server with the fewest active connections. The next request can be sent to Server B:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5_9G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5_9G!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!5_9G!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!5_9G!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!5_9G!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!5_9G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:592491,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5_9G!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!5_9G!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!5_9G!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!5_9G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F875f276c-c676-47b4-9f5e-84815f83c006_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This can be useful when requests have significantly different processing times.</p><h2>IP Hash</h2><p>The load balancer calculates a hash based on the client's IP address.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Elsr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Elsr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Elsr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Elsr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Elsr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Elsr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ef42661-139e-485e-963c-22e060e8f938_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:591271,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Elsr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Elsr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Elsr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Elsr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef42661-139e-485e-963c-22e060e8f938_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The same client can therefore tend to reach the same backend server.</p><p>This can be useful when applications have session-related requirements, although modern systems often prefer <strong>shared session storage</strong> instead of relying on server affinity.</p><div><hr></div><h1>&#127760; Layer 4 Load Balancer (Network Level)</h1><p>Layer 4 operates primarily with:</p><ul><li><p>TCP</p></li><li><p>UDP</p></li><li><p>IP</p></li><li><p>Port</p></li></ul><p>It doesn&#8217;t need to understand the HTTP request itself.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Q4O7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Q4O7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Q4O7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Q4O7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Q4O7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Q4O7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/befadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:576197,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Q4O7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!Q4O7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!Q4O7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!Q4O7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbefadab4-1129-4b3e-87c1-c5bd3df484ff_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Advantages</h3><ul><li><p>Fast</p></li><li><p>Low overhead</p></li><li><p>Suitable for TCP/UDP workloads</p></li><li><p>Protocol agnostic</p></li></ul><h1>&#128421;&#65039; Layer 7 Load Balancer(Application Level)</h1><p>Layer 7 understands application-level protocols such as HTTP/HTTPS.</p><p>Therefore, it can make routing decisions based on:</p><ul><li><p>URL path</p></li><li><p>HTTP method</p></li><li><p>Headers</p></li><li><p>Cookies</p></li><li><p>Hostname</p></li></ul><p>For example:</p><pre><code><code>/api/users/*    &#8594; User Service

/api/orders/*   &#8594; Order Service

/api/payments/* &#8594; Payment Service</code></code></pre><p>Architecture:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x1I9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x1I9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!x1I9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!x1I9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!x1I9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!x1I9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:636319,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/218167634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!x1I9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!x1I9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!x1I9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!x1I9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F56dcb539-63c6-4877-8733-ed5ba9277530_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This makes Layer 7 load balancing particularly useful for <strong>microservices and API-based architectures</strong>.</p><h1>&#127919; Final Takeaway</h1><p>When designing a distributed application, don&#8217;t ask only:</p><blockquote><p><strong>&#8220;How many servers do I need?&#8221;</strong></p></blockquote><p>Ask:</p><blockquote><p><strong>&#8220;How will traffic enter the system, how will it be distributed, how will failures be detected, and how will the system continue operating when individual components fail?&#8221;</strong></p></blockquote><p>That&#8217;s where the load balancer becomes an important architectural building block.</p><p>A well-designed load-balancing layer helps transform a single-server application into a <strong>scalable, resilient, and highly available distributed system</strong>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚪API Gateway: The Front Door of Your Microservices Architecture]]></title><description><![CDATA[The essential building block for secure, scalable, and manageable microservices.]]></description><link>https://newsletter.priteshbhanushali.com/p/api-gateway-the-front-door-of-your</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/api-gateway-the-front-door-of-your</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Thu, 01 Oct 2026 05:01:50 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ce0e343b-e047-448e-bd03-1a562d3df98a_1310x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you have an e-commerce application with multiple services:</p><ul><li><p>&#128100; User Service</p></li><li><p>&#128722; Order Service</p></li><li><p>&#128179; Payment Service</p></li><li><p>&#128230; Inventory Service</p></li><li><p>&#128276; Notification Service</p></li></ul><p>Should the frontend communicate directly with every service?</p><p>Usually, <strong>no.</strong></p><p>This is where an <strong>API Gateway</strong> comes into the picture.</p><h2>What is an API Gateway?</h2><p>An <strong>API Gateway</strong> is a single entry point between clients and backend services.</p><p>Instead of:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Jn2j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Jn2j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 424w, https://substackcdn.com/image/fetch/$s_!Jn2j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 848w, https://substackcdn.com/image/fetch/$s_!Jn2j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 1272w, https://substackcdn.com/image/fetch/$s_!Jn2j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Jn2j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png" width="1237" height="1010" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1010,&quot;width&quot;:1237,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:137049,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/217831820?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Jn2j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 424w, https://substackcdn.com/image/fetch/$s_!Jn2j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 848w, https://substackcdn.com/image/fetch/$s_!Jn2j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 1272w, https://substackcdn.com/image/fetch/$s_!Jn2j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F44958ab6-20f0-4f5a-a62e-5b38b9251e35_1237x1010.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>we use:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t-hd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t-hd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 424w, https://substackcdn.com/image/fetch/$s_!t-hd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 848w, https://substackcdn.com/image/fetch/$s_!t-hd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 1272w, https://substackcdn.com/image/fetch/$s_!t-hd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!t-hd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png" width="1237" height="1010" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1010,&quot;width&quot;:1237,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:145011,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/217831820?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!t-hd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 424w, https://substackcdn.com/image/fetch/$s_!t-hd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 848w, https://substackcdn.com/image/fetch/$s_!t-hd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 1272w, https://substackcdn.com/image/fetch/$s_!t-hd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4d3a0b1-6549-490a-a448-47b8e1f51ff8_1237x1010.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The client communicates with the gateway, and the gateway routes the request to the appropriate backend service.</p><h2>Why Do We Need an API Gateway?</h2><p>As your system grows, several common requirements appear:</p><h3>1. Request Routing</h3><p>The gateway determines where a request should go.</p><pre><code><code>GET /api/users   &#8594; User Service
GET /api/orders  &#8594; Order Service
POST /api/payment &#8594; Payment Service</code></code></pre><h3>2. Authentication &amp; Authorization &#128272;</h3><p>Instead of implementing authentication logic independently in every service, the gateway can validate:</p><ul><li><p>JWT tokens</p></li><li><p>API keys</p></li><li><p>OAuth tokens</p></li><li><p>Access permissions</p></li></ul><pre><code><code>Client
  &#8595;
API Gateway
  &#8595; Validate Token
  &#8595;
Backend Service</code></code></pre><p>However, services should still enforce authorization for sensitive operations rather than blindly trusting the gateway.</p><h3>3. Rate Limiting &#128678;</h3><p>The gateway can prevent clients from sending too many requests.</p><p>For example:</p><pre><code><code>100 requests / minute / user</code></code></pre><p>If the limit is exceeded:</p><pre><code><code>HTTP 429 Too Many Requests</code></code></pre><p>This helps protect backend services from accidental or abusive traffic.</p><h3>4. Logging &amp; Monitoring &#128202;</h3><p>A gateway provides a centralized place to capture:</p><ul><li><p>Request/response metrics</p></li><li><p>Latency</p></li><li><p>Error rates</p></li><li><p>Request IDs</p></li><li><p>Traffic patterns</p></li></ul><p>This makes troubleshooting distributed systems easier.</p><h3>5. Response Aggregation</h3><p>Sometimes a frontend needs data from multiple services.</p><p>Instead of making multiple requests:</p><pre><code><code>Client &#8594; User Service
Client &#8594; Order Service
Client &#8594; Payment Service</code></code></pre><p>the gateway can orchestrate the calls:</p><pre><code><code>Client
  &#8595;
API Gateway
  &#9500;&#9472;&#9472;&#8594; User Service
  &#9500;&#9472;&#9472;&#8594; Order Service
  &#9492;&#9472;&#9472;&#8594; Payment Service
        &#8595;
   Combined Response</code></code></pre><p>This can reduce client-side complexity and network round trips, although excessive aggregation can make the gateway harder to maintain.</p><h1>&#128736;&#65039; Popular API Gateway Solutions</h1><p>Some commonly used options are:</p><ul><li><p><strong>Kong</strong></p></li><li><p><strong>NGINX</strong></p></li><li><p><strong>AWS API Gateway</strong></p></li><li><p><strong>Azure API Management</strong></p></li><li><p><strong>Google Cloud API Gateway</strong></p></li></ul><p>The right choice depends on your infrastructure, traffic requirements, cloud platform, and operational needs.</p><h1>&#127386; API Gateway vs Load Balancer</h1><p>These two are often confused.</p><p>A <strong>Load Balancer</strong> primarily distributes traffic across healthy backend instances.</p><p>An <strong>API Gateway</strong> generally operates at a higher application/API layer and can provide capabilities such as authentication, rate limiting, routing, transformation, and API policies.</p><p>In many architectures, you can have both:</p><pre><code><code>Client
  &#8595;
Load Balancer
  &#8595;
API Gateway
  &#8595;
Microservices</code></code></pre><p>Or the gateway itself may incorporate load-balancing capabilities.</p><h1>&#9878;&#65039; The Trade-off</h1><p>API Gateway provides many benefits, but it also introduces another component.</p><p>Potential challenges include:</p><ul><li><p>Additional latency</p></li><li><p>Gateway becoming a bottleneck</p></li><li><p>More operational complexity</p></li><li><p>Configuration management</p></li><li><p>Failure handling</p></li><li><p>Overloading the gateway with business logic</p></li></ul><p><strong>A good API Gateway should handle cross-cutting concerns not become your entire application.</strong></p><h1>&#127919; Final Thought</h1><p>In a microservices architecture, the API Gateway acts as the <strong>front door</strong> to your backend.</p><p>It can simplify:</p><p><strong>Routing + Security + Rate Limiting + Observability + Traffic Management</strong></p><p>But the goal isn&#8217;t to put everything inside the gateway.</p><blockquote><p><strong>Keep the gateway focused on managing API traffic, while business logic remains inside the appropriate services.</strong></p></blockquote><p>That&#8217;s the key to building a scalable and maintainable API architecture.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[CAP: The Distributed Systems Trade-off]]></title><description><![CDATA[Consistency, Availability, and What Happens When Networks Fail]]></description><link>https://newsletter.priteshbhanushali.com/p/cap-the-distributed-systems-trade</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/cap-the-distributed-systems-trade</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Mon, 28 Sep 2026 05:01:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!o1Qm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you&#8217;re running a global e-commerce platform.</p><p>A customer in Mumbai clicks <strong>&#8220;Pay Now.&#8221;</strong></p><p>The request reaches your India region:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S4mK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S4mK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!S4mK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!S4mK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!S4mK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S4mK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:469990,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/217578149?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!S4mK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!S4mK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!S4mK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!S4mK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf7c7ced-3afd-401b-8f26-297e698a01e8_1024x559.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Everything has worked perfectly for months.</p><p>Then, during a massive sale, something goes wrong.</p><p>The network connection between Mumbai and Singapore breaks.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ymN6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ymN6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ymN6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ymN6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ymN6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ymN6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:665777,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/217578149?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ymN6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ymN6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ymN6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ymN6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9649f32c-0966-42b5-bd04-81f5b86890df_1024x559.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Now suppose the customer tries to purchase the <strong>last available item</strong>.</p><p>Mumbai says:</p><blockquote><p>&#8220;Inventory = 1. I&#8217;ll sell it.&#8221;</p></blockquote><p>Singapore simultaneously says:</p><blockquote><p>&#8220;Inventory = 1. I&#8217;ll sell it.&#8221;</p></blockquote><p>You now potentially have:</p><p>You now potentially have:</p><pre><code><code>Mumbai:    inventory = 0
Singapore: inventory = 0

But:

Orders created = 2
Actual items  = 1</code></code></pre><p>Congratulations.</p><p>You&#8217;ve just encountered one of the fundamental problems of distributed systems.</p><p>This is where <strong>CAP Theorem</strong> becomes practical.</p><p>And this is also where one of the most commonly repeated explanations of CAP becomes misleading:</p><blockquote><p>&#10060; &#8220;You can pick any two of Consistency, Availability, and Partition Tolerance.&#8221;</p></blockquote><p>That&#8217;s not really how CAP should be understood.</p><p>The useful mental model is:</p><blockquote><p><strong>When a network partition happens, a distributed system must trade off strong consistency against availability.</strong></p></blockquote><p>Let&#8217;s unpack why.</p><h2>What is CAP Theorem?</h2><p>CAP stands for:</p><ul><li><p><strong>C &#8212; Consistency</strong></p></li><li><p><strong>A &#8212; Availability</strong></p></li><li><p><strong>P &#8212; Partition Tolerance</strong></p></li></ul><p>The theorem says that a distributed system cannot guarantee <strong>strong consistency and availability simultaneously when a network partition occurs</strong>.</p><p>The common explanation:</p><blockquote><p>&#10060; &#8220;Pick any 2 out of 3.&#8221;</p></blockquote><p>is misleading.</p><p>The more useful mental model is:</p><pre><code><code>          Network Partition
                  |
             +----+----+
             |         |
             v         v
        Consistency  Availability
             CP          AP</code></code></pre><p>When a partition happens, you have to make a trade-off between <strong>C and A</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o1Qm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o1Qm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!o1Qm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!o1Qm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!o1Qm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!o1Qm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1646596,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/217578149?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!o1Qm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!o1Qm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!o1Qm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!o1Qm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd16a3c86-8f5f-4c28-889a-9b434ffc9446_1536x1024.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>1. Consistency</h2><p>CAP consistency generally means <strong>linearizability</strong>.</p><p>After a successful write, subsequent reads should see that value or a newer value.</p><pre><code><code>WRITE balance = &#8377;500
        |
        v
READ balance
        |
        v
     &#8377;500</code></code></pre><p>Important:</p><blockquote><p>CAP Consistency is <strong>not the same as the C in ACID</strong>.</p></blockquote><div><hr></div><h2>2. Availability</h2><p>Availability means that every request sent to a non-failing node receives a <strong>non-error response</strong>.</p><p>During a network failure, an available system continues serving requests.</p><p>The trade-off is that the response may not reflect the latest global state.</p><div><hr></div><h2>3. Partition Tolerance</h2><p>A partition occurs when distributed nodes cannot reliably communicate.</p><pre><code><code>Node A                  Node B

  DB                      DB
   |                       |
   XXXXXXXXXXXXXXXXXXXXXXXX
          Network
          Partition</code></code></pre><p>Network failures, packet loss, latency spikes, cloud outages, and routing problems make partitions a reality.</p><p>Therefore:</p><blockquote><p><strong>Partition tolerance is not really an optional configuration in a distributed system.</strong></p></blockquote><div><hr></div><h1>CP vs AP</h1><h3>CP &#8212; Consistency + Partition Tolerance</h3><p>During a partition, the system may reject or block some requests rather than risk inconsistent data.</p><pre><code><code>Client
  |
  v
Node A
  |
  X ---- Node B
  |
 ERROR</code></code></pre><p><strong>Typical use cases:</strong></p><ul><li><p>Financial transactions</p></li><li><p>Distributed locks</p></li><li><p>Leader election</p></li><li><p>Inventory reservation</p></li><li><p>Configuration/coordination</p></li></ul><p>Examples include <strong>ZooKeeper, etcd</strong>, and appropriately configured MongoDB deployments.</p><div><hr></div><h3>AP &#8212; Availability + Partition Tolerance</h3><p>During a partition, the system continues serving requests even if replicas temporarily disagree.</p><pre><code><code>Node A                 Node B

stock = 0              stock = 1
   |                       |
   XXXXXXXXXXXXXXXXXXXXXXXX
          Partition</code></code></pre><p>When the network recovers, the system reconciles the differences.</p><p><strong>Typical use cases:</strong></p><ul><li><p>Social feeds</p></li><li><p>Shopping carts</p></li><li><p>Large-scale analytics</p></li><li><p>Highly available applications</p></li></ul><p>Examples include <strong>Apache Cassandra</strong> and systems using eventual-consistency patterns such as DynamoDB&#8217;s eventually consistent reads.</p><div><hr></div><h1>How Should Engineers Think About CAP?</h1><p>Don&#8217;t simply ask:</p><blockquote><p>&#8220;Is this database CP or AP?&#8221;</p></blockquote><p>Instead ask:</p><h4>1. What happens if the network fails?</h4><h4>2. Can stale data cause business damage?</h4><h4>3. Can two nodes safely make independent decisions?</h4><h4>4. Can conflicting updates be merged?</h4><h4>5. How much latency is acceptable?</h4><p>For example:</p><pre><code><code>Payment authorization &#8594; Strong consistency

Inventory reservation &#8594; Strong consistency

Social feed           &#8594; Eventual consistency

Analytics dashboard   &#8594; Eventual consistency

Shopping cart         &#8594; Often eventual/tunable</code></code></pre><h1>3 Key Takeaways</h1><p><strong>1. CAP does NOT mean &#8220;pick any 2 of 3.&#8221;</strong></p><p>The important trade-off happens <strong>when a network partition occurs: Consistency vs Availability</strong>.</p><p><strong>2. Partition tolerance is a reality.</strong></p><p>If your application is distributed across machines or regions, network failures must be part of your design.</p><p><strong>3. Choose consistency per business requirement.</strong></p><p>Don&#8217;t make your entire architecture &#8220;CP&#8221; or &#8220;AP&#8221; blindly. Determine which data requires strong consistency and which data can tolerate temporary staleness.</p><blockquote><p><strong>The real question isn&#8217;t &#8220;CP or AP?&#8221;</strong></p><p><strong>It&#8217;s &#8220;What must remain correct when the network fails?&#8221;</strong></p></blockquote><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[⏱️ Choose the right read and write patterns to scale your application from thousands to millions of requests.]]></title><description><![CDATA[Beyond Cache::remember(): Advanced Laravel Caching Architectures]]></description><link>https://newsletter.priteshbhanushali.com/p/choose-the-right-read-and-write-patterns</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/choose-the-right-read-and-write-patterns</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Mon, 21 Sep 2026 05:01:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-Ml1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Caching Patterns explain with real world scenarios In laravel with best practices</p><p>But adding <code>Cache::remember()</code> to a database query is not a complete caching strategy.</p><p>As an application grows, caching introduces important questions:</p><ul><li><p>What data should be cached?</p></li><li><p>When should the cache be invalidated?</p></li><li><p>What happens when the database changes?</p></li><li><p>What if multiple users update the same data?</p></li><li><p>How do we prevent stale data?</p></li><li><p>What happens when Redis goes down?</p></li><li><p>How do we avoid a cache stampede?</p></li><li><p>Should we update the cache before or after updating the database?</p></li></ul><p>These questions are where <strong>caching patterns</strong> become important.</p><p>In this article, we&#8217;ll explore the most common caching patterns and see how they can be implemented in Laravel using real-world scenarios.</p><p>.</p><div><hr></div><h1>&#128640; What Is Caching?</h1><p>Caching is the process of temporarily storing frequently accessed data in a faster storage layer so that future requests can retrieve it without repeating an expensive operation.</p><p>Avoid doing expensive work repeatedly when the result can be reused safely.</p><h1>&#129504; Why Do We Need Caching Patterns?</h1><p>Caching sounds simple:</p><pre><code><code>Cache::remember('users', 3600, function () {
    return User::all();
});</code></code></pre><p>But real applications are more complicated.</p><p>Imagine an insurance platform where thousands of users frequently request:</p><ul><li><p>Product configuration</p></li><li><p>State tax rates</p></li><li><p>Policy rules</p></li><li><p>Producer information</p></li><li><p>Permission configuration</p></li></ul><p>Some of this data changes frequently, while some changes only once a day or once a month.</p><p>Using the same caching strategy for everything can create problems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-Ml1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-Ml1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!-Ml1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!-Ml1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!-Ml1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!-Ml1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:732647,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216484273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-Ml1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!-Ml1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!-Ml1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!-Ml1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1722a22-edcc-48d5-9594-d472118c4c1c_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is why we need different caching patterns.</p><h1>&#128218; Common Caching Patterns</h1><p>The most useful caching patterns for Laravel applications are:</p><ol><li><p>Cache-Aside</p></li><li><p>Read-Through</p></li><li><p>Write-Through</p></li><li><p>Write-Behind / Write-Back</p></li><li><p>Refresh-Ahead</p></li><li><p>Stale-While-Revalidate</p></li><li><p>Cache-Through for External APIs</p></li><li><p>Cache-Aside with Event-Based Invalidation</p></li></ol><h1>1&#65039;&#8419; Cache-Aside Pattern</h1><h2>The Most Common Pattern in Laravel</h2><p>The <strong>Cache-Aside</strong> pattern is also called <strong>Lazy Loading</strong>.</p><p>The application is responsible for checking the cache first.</p><p>If the data exists:</p><pre><code><code>Cache HIT
   &#8595;
Return cached data</code></code></pre><p>If the data does not exist:</p><pre><code><code>Cache MISS
   &#8595;
Query Database
   &#8595;
Store Result in Cache
   &#8595;
Return Result</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nsIn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nsIn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!nsIn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!nsIn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!nsIn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!nsIn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:635889,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216484273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nsIn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!nsIn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!nsIn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!nsIn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6eb67be1-9202-4cfc-b85e-aa5b276628ec_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Example</h2><pre><code><code>$products = Cache::remember(
    'products',
    now()-&gt;addHour(),
    function () {
        return Product::where('active', true)-&gt;get();
    }
);</code></code></pre><p>Laravel will:</p><ol><li><p>Check the cache.</p></li><li><p>Return the cached value if available.</p></li><li><p>Execute the callback on a cache miss.</p></li><li><p>Store the result in the cache.</p></li><li><p>Return the result.</p></li></ol><h2>When Should You Use Cache-Aside?</h2><p>Cache-Aside is a good choice when:</p><ul><li><p>Data is read frequently.</p></li><li><p>Data changes relatively infrequently.</p></li><li><p>You can tolerate a small amount of staleness.</p></li><li><p>The application controls cache invalidation.</p></li></ul><p>Examples:</p><ul><li><p>Product catalog</p></li><li><p>Application configuration</p></li><li><p>User permissions</p></li><li><p>Tax rates</p></li><li><p>Country/state lists</p></li><li><p>Dashboard statistics</p></li></ul><div><hr></div><h1>2&#65039;&#8419; Read-Through Cache</h1><p>Read-Through caching moves the responsibility of loading missing data closer to the caching layer.</p><p>Conceptually:</p><pre><code><code>Application
     &#8595;
   Cache
     &#8595;
Cache MISS
     &#8595;
Data Store
     &#8595;
Cache
     &#8595;
Application</code></code></pre><p>The application doesn&#8217;t explicitly manage the cache-miss loading logic.</p><p>Instead, the caching abstraction handles it.</p><h3>Conceptual Example</h3><pre><code><code>$product = $cache-&gt;get(
    'product:1001',
    fn () =&gt; Product::find(1001)
);</code></code></pre><p>The cache abstraction decides what to do when the value isn&#8217;t available.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ja8v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ja8v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ja8v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ja8v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ja8v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ja8v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:634687,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216484273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ja8v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!ja8v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!ja8v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!ja8v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9a355a68-262f-4bb6-bfa8-77087080f523_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Example</h2><p>Laravel applications commonly implement this behavior using service classes around Laravel&#8217;s cache abstraction.</p><p>For example:</p><pre><code><code>class ProductService
{
    public function find(int $id): ?Product
    {
        return Cache::remember(
            "product:{$id}",
            now()-&gt;addMinutes(30),
            fn () =&gt; Product::find($id)
        );
    }
}</code></code></pre><p>Although Laravel&#8217;s <code>Cache::remember()</code> is often described as Cache-Aside, a service layer can encapsulate that behavior so callers don&#8217;t need to know how the cache is populated.</p><div><hr></div><h1>3&#65039;&#8419; Write-Through Cache</h1><p>With <strong>Write-Through caching</strong>, data is written to the cache and persistent storage as part of the write operation.</p><p>The general flow is:</p><pre><code><code>Application
     &#8595;
   Cache
     &#8595;
 Database</code></code></pre><p>The cache is updated immediately when the data changes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i36-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i36-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!i36-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!i36-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!i36-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!i36-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:648400,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216484273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i36-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!i36-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!i36-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!i36-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ef392a7-c0b2-4ad6-bf53-463124db529e_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Example</h2><p>Suppose an administrator updates a product price.</p><pre><code><code>$product-&gt;update([
    'price' =&gt; $price,
]);

Cache::put(
    "product:{$product-&gt;id}",
    $product-&gt;fresh(),
    now()-&gt;addHour()
);</code></code></pre><p>Now both the database and cache contain the latest value.</p><h2>Important Consideration</h2><p>Write-through caching increases write complexity.</p><p>You need to think about:</p><ul><li><p>What happens if the database update succeeds but cache update fails?</p></li><li><p>What happens if cache update succeeds but database update fails?</p></li><li><p>Which system is considered the source of truth?</p></li></ul><p>In most business applications:</p><blockquote><p><strong>The database should remain the source of truth.</strong></p></blockquote><p>The cache should be treated as a performance optimization, not the authoritative data store.</p><div><hr></div><h1>4&#65039;&#8419; Write-Behind / Write-Back Cache</h1><p>Write-Behind caching takes the idea further.</p><p>The application writes to the cache first, while the persistent database update happens asynchronously.</p><pre><code><code>Application
     &#8595;
   Cache
     &#8595;
Queue
     &#8595;
Worker
     &#8595;
Database</code></code></pre><p>This can make write-heavy systems faster because the user doesn&#8217;t always need to wait for the database operation.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aLBo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aLBo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!aLBo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!aLBo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!aLBo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!aLBo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:710348,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216484273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aLBo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!aLBo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!aLBo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!aLBo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59e00889-c746-41fd-8f21-6b154aeb157a_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Example</h2><p>A simplified implementation might look like:</p><pre><code><code>Cache::put(
    "counter:{$userId}",
    $newValue,
    now()-&gt;addHours(1)
);

UpdateCounter::dispatch($userId, $newValue);</code></code></pre><p>The queue worker later persists the value.</p><h2>Real-World Scenario</h2><p>Consider a website tracking:</p><pre><code><code>Page Views
Product Views
Video Views
Click Events</code></code></pre><p>Writing every event synchronously to MySQL can create significant database load.</p><p>Instead:</p><pre><code><code>User Activity
     &#8595;
Redis
     &#8595;
Queue
     &#8595;
Batch Processing
     &#8595;
MySQL</code></code></pre><p>This approach can dramatically reduce database write pressure.</p><h2>&#9888;&#65039; The Risk</h2><p>Write-behind caching introduces eventual consistency.</p><p>For example:</p><pre><code><code>Redis = 10,500 views
Database = 10,200 views</code></code></pre><p>The database may temporarily lag behind the cache.</p><p>Therefore, this pattern is more appropriate for data where eventual consistency is acceptable.</p><p>It should generally <strong>not</strong> be used casually for critical financial transactions, account balances, or other data where every write must be durably committed before success is reported.</p><div><hr></div><h1>5&#65039;&#8419; Refresh-Ahead Pattern</h1><p>Refresh-Ahead caching attempts to refresh cached data <strong>before it expires</strong>.</p><p>Without Refresh-Ahead:</p><pre><code><code>Cache expires
     &#8595;
First user gets Cache MISS
     &#8595;
Database query
     &#8595;
Cache recreated</code></code></pre><p>The first request after expiration can become slower.</p><p>With Refresh-Ahead:</p><pre><code><code>Cache nearly expires
       &#8595;
Background refresh
       &#8595;
New cache value
       &#8595;
Users continue receiving cached data</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jIh6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jIh6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!jIh6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!jIh6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!jIh6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!jIh6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:755107,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216484273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jIh6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!jIh6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!jIh6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!jIh6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01f0c259-1c06-482b-a30c-2ffff9f5bdd5_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Real-World Scenario</h2><p>Imagine a dashboard showing:</p><pre><code><code>Daily Revenue
Active Policies
Open Claims
Pending Payments</code></code></pre><p>Suppose the dashboard is requested heavily between:</p><pre><code><code>9:00 AM - 6:00 PM</code></code></pre><p>You don&#8217;t necessarily want the first user at 9:00 AM to trigger a large aggregation query.</p><p>Instead, a scheduled job can refresh the cache before users need it.</p><p>Laravel scheduler:</p><pre><code><code>Schedule::job(RefreshDashboardCache::class)
    -&gt;everyTenMinutes();</code></code></pre><p>The job could calculate the latest dashboard metrics and update the cache.</p><div><hr></div><h1>6&#65039;&#8419; Stale-While-Revalidate</h1><p>One of the most useful patterns for high-traffic applications is:</p><blockquote><p><strong>Stale-While-Revalidate</strong></p></blockquote><p>The idea is simple:</p><pre><code><code>Fresh Data
   &#8595;
Return immediately

Stale but usable
   &#8595;
Return stale data
   +
Refresh in background

Expired
   &#8595;
Generate fresh data</code></code></pre><p>This avoids making users wait for expensive cache regeneration.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_j8Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_j8Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_j8Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_j8Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_j8Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!_j8Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:770517,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216484273?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_j8Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!_j8Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!_j8Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!_j8Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23a15f43-d3ed-43c4-8e3d-dd4841b45254_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Laravel Example</h2><p>Laravel provides support for stale-while-revalidate behavior through <code>Cache::flexible()</code>.</p><pre><code><code>$data = Cache::flexible(
    'dashboard',
    [300, 600],
    function () {
        return $this-&gt;generateDashboard();
    }
);</code></code></pre><p>The two values represent the fresh and stale periods.</p><h2>Why Is This Powerful?</h2><p>Imagine generating a report takes:</p><pre><code><code>2 seconds</code></code></pre><p>And 500 users request the same report.</p><p>Without stale-while-revalidate:</p><pre><code><code>Cache expires
     &#8595;
Users wait for regeneration</code></code></pre><p>With stale-while-revalidate:</p><pre><code><code>Existing stale result
     &#8595;
Return immediately
     &#8595;
Refresh asynchronously</code></code></pre><p>Users get a much smoother experience.</p><div><hr></div><h1>7&#65039;&#8419; Cache External API Responses</h1><p>Caching isn&#8217;t limited to databases.</p><p>External APIs can also be expensive and slow.</p><p>Consider:</p><pre><code><code>Laravel
   &#8595;
Tax API
   &#8595;
External Service</code></code></pre><p>If the same tax information is requested repeatedly, calling the external API for every request is unnecessary.</p><p>Instead:</p><pre><code><code>$taxRate = Cache::remember(
    "tax-rate:{$state}:{$year}",
    now()-&gt;addDay(),
    function () use ($state, $year) {
        return $this-&gt;taxApi-&gt;getRate($state, $year);
    }
);</code></code></pre><p>Now:</p><pre><code><code>Request 1 &#8594; API
Request 2 &#8594; Cache
Request 3 &#8594; Cache
Request 4 &#8594; Cache</code></code></pre><h2>Real-World Scenario</h2><p>Suppose an application needs:</p><pre><code><code>Currency Exchange Rates
Tax Rates
Shipping Rates
Weather Information
Third-Party Configuration</code></code></pre><p>These values may not need to be fetched from the external provider on every request.</p><p>Caching can:</p><ul><li><p>Reduce API usage</p></li><li><p>Reduce latency</p></li><li><p>Reduce external dependency</p></li><li><p>Protect against temporary API failures</p></li></ul><div><hr></div><h1>8&#65039;&#8419; Event-Based Cache Invalidation</h1><p>One of the hardest problems in caching is:</p><blockquote><p><strong>When should the cache be invalidated?</strong></p></blockquote><p>Consider:</p><pre><code><code>$product = Cache::remember(
    "product:{$id}",
    3600,
    fn () =&gt; Product::find($id)
);</code></code></pre><p>Now the product changes.</p><p>If you don&#8217;t remove the cache:</p><pre><code><code>Database
Price = &#8377;999

Cache
Price = &#8377;799</code></code></pre><p>The application may return incorrect information until the TTL expires.</p><h2>Use Events</h2><p>Laravel events can help coordinate cache invalidation.</p><p>For example:</p><pre><code><code>class ProductUpdated
{
    public function __construct(
        public Product $product
    ) {}
}</code></code></pre><p>Listener:</p><pre><code><code>class ClearProductCache
{
    public function handle(ProductUpdated $event): void
    {
        Cache::forget(
            "product:{$event-&gt;product-&gt;id}"
        );
    }
}</code></code></pre><p>Now:</p><pre><code><code>Product Updated
      &#8595;
Event
      &#8595;
Invalidate Cache
      &#8595;
Next request
      &#8595;
Database
      &#8595;
Repopulate Cache</code></code></pre><p>This is a clean implementation of Cache-Aside with event-driven invalidation.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🧩 GraphQL in Laravel: The Modern Approach to API Development]]></title><description><![CDATA[Beyond REST: Discovering the Power of GraphQL in Laravel]]></description><link>https://newsletter.priteshbhanushali.com/p/graphql-in-laravel-the-modern-approach</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/graphql-in-laravel-the-modern-approach</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 19 Sep 2026 05:01:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!0Wlo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0Wlo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0Wlo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 424w, https://substackcdn.com/image/fetch/$s_!0Wlo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 848w, https://substackcdn.com/image/fetch/$s_!0Wlo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!0Wlo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0Wlo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png" width="1310" height="1200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1200,&quot;width&quot;:1310,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1428315,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/216312179?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0Wlo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 424w, https://substackcdn.com/image/fetch/$s_!0Wlo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 848w, https://substackcdn.com/image/fetch/$s_!0Wlo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!0Wlo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0678ac7b-146e-4a05-b6dc-a5400b8748ec_1310x1200.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Building an API is easy.</p><p>Building an API that continues to work well as your application grows, your frontend requirements change, and multiple clients consume the same backend&#8212;that&#8217;s where things become interesting.</p><p>Imagine you&#8217;re building an e-commerce platform with three different clients:</p><ul><li><p>A React web application</p></li><li><p>A Flutter mobile application</p></li><li><p>An admin dashboard</p></li></ul><p>All three applications use the same Laravel backend, but they don&#8217;t necessarily need the same data.</p><p>For example, the mobile application might only need a product&#8217;s basic information:</p><pre><code><code>Product
&#9500;&#9472;&#9472; id
&#9500;&#9472;&#9472; name
&#9500;&#9472;&#9472; price
&#9492;&#9472;&#9472; image</code></code></pre><p>The product listing page might need:</p><pre><code><code>Product
&#9500;&#9472;&#9472; id
&#9500;&#9472;&#9472; name
&#9500;&#9472;&#9472; price
&#9500;&#9472;&#9472; image
&#9500;&#9472;&#9472; rating
&#9492;&#9472;&#9472; stock</code></code></pre><p>While the admin dashboard might need considerably more:</p><pre><code><code>Product
&#9500;&#9472;&#9472; id
&#9500;&#9472;&#9472; name
&#9500;&#9472;&#9472; description
&#9500;&#9472;&#9472; price
&#9500;&#9472;&#9472; stock
&#9500;&#9472;&#9472; categories
&#9500;&#9472;&#9472; reviews
&#9500;&#9472;&#9472; supplier
&#9500;&#9472;&#9472; sales
&#9492;&#9472;&#9472; inventory</code></code></pre><p>This creates an interesting API design problem.</p><p>With a traditional REST approach, you might start with an endpoint such as:</p><pre><code><code>GET /api/products/100</code></code></pre><p>But as different clients require different representations of the same resource, you may eventually find yourself adding more endpoints, query parameters, response transformations, or specialized APIs.</p><p>You can also run into two common problems.</p><h3>&#11088; Over-fetching</h3><p>The API returns more data than the client needs.</p><pre><code><code>{
    "id": 100,
    "name": "MacBook Pro",
    "price": 1999,
    "description": "...",
    "stock": 25,
    "supplier": "...",
    "created_at": "...",
    "updated_at": "..."
}</code></code></pre><p>The mobile application might only need:</p><pre><code><code>{
    "id": 100,
    "name": "MacBook Pro",
    "price": 1999
}</code></code></pre><p>The remaining fields are unnecessary for that particular screen.</p><h3>&#11088; Under-fetching</h3><p>The opposite problem can also occur.</p><p>Suppose a customer details page needs:</p><pre><code><code>Customer
 &#9500;&#9472;&#9472; Profile
 &#9500;&#9472;&#9472; Orders
 &#9474;    &#9492;&#9472;&#9472; Order Items
 &#9474;          &#9492;&#9472;&#9472; Products
 &#9492;&#9472;&#9472; Payments</code></code></pre><p>With REST, this might require several API calls:</p><pre><code><code>GET /api/customers/10
GET /api/customers/10/orders
GET /api/orders/100/items
GET /api/customers/10/payments</code></code></pre><p>The frontend now has to make multiple requests and combine the results.</p><p>This is where GraphQL becomes interesting.</p><p>Instead of defining the response entirely on the server, GraphQL allows the client to describe <strong>exactly what data it needs</strong>.</p><p>For example:</p><pre><code><code>query {
    customer(id: 10) {
        id
        name

        orders {
            id
            orderNumber

            items {
                quantity
                product {
                    id
                    name
                    price
                }
            }
        }

        payments {
            amount
            status
        }
    }
}</code></code></pre><p>The server returns a response that follows the exact structure requested by the client.</p><pre><code><code>{
    "data": {
        "customer": {
            "id": "10",
            "name": "John Doe",
            "orders": [
                {
                    "id": "100",
                    "orderNumber": "ORD-1001",
                    "items": [
                        {
                            "quantity": 2,
                            "product": {
                                "id": "50",
                                "name": "MacBook Pro",
                                "price": 1999
                            }
                        }
                    ]
                }
            ],
            "payments": [
                {
                    "amount": 3998,
                    "status": "PAID"
                }
            ]
        }
    }
}</code></code></pre><p>The important idea is not simply that GraphQL can retrieve data through a single endpoint.</p><p>The bigger idea is this:</p><blockquote><p><strong>GraphQL allows the client to describe the shape of the data it needs, while the server controls what data and operations are actually available.</strong></p></blockquote><p>This makes GraphQL particularly interesting for applications with multiple clients, complex relationships, and rapidly changing frontend requirements.</p><p>But GraphQL is not a replacement for REST in every situation.</p><p>It introduces its own challenges, including:</p><ul><li><p>Schema design</p></li><li><p>Authorization</p></li><li><p>N+1 database queries</p></li><li><p>Query complexity</p></li><li><p>Query depth</p></li><li><p>Pagination</p></li><li><p>Caching</p></li><li><p>Security</p></li><li><p>Schema evolution</p></li></ul><p>So the real question isn&#8217;t:</p><blockquote><p><strong>&#8220;Is GraphQL better than REST?&#8221;</strong></p></blockquote><h2>&#9889; Why Was GraphQL Created ?</h2><p>Traditional REST APIs generally expose resources through URLs.</p><p>For example:</p><pre><code><code>GET /users/1
GET /users/1/orders
GET /users/1/orders/10/items</code></code></pre><p>As applications become more complex, clients can end up making many requests to construct a single screen.</p><p>Consider an order details page.</p><p>The page needs:</p><pre><code><code>Customer
Order
Order Items
Products
Shipping Address
Payment</code></code></pre><p>A REST implementation might require:</p><pre><code><code>GET /customers/10
GET /customers/10/orders/100
GET /orders/100/items
GET /orders/100/shipping-address
GET /orders/100/payment</code></code></pre><p>GraphQL allows the client to describe this relationship as a single query:</p><pre><code><code>query {
    order(id: 100) {
        id
        orderNumber
        status

        customer {
            id
            name
            email
        }

        items {
            quantity
            price

            product {
                id
                name
            }
        }

        shippingAddress {
            city
            state
            country
        }

        payment {
            status
            amount
        }
    }
}</code></code></pre><p>This is one of the most powerful ideas behind GraphQL.</p><h1>&#128311; GraphQL Core Concepts</h1><p>Before implementing GraphQL in Laravel, you need to understand a few fundamental concepts.</p><p>The most important ones are:</p><pre><code><code>Schema
Type
Query
Mutation
Field
Argument
Input
Resolver
Scalar
Enum
Interface
Union
Subscription</code></code></pre><p>Let&#8217;s understand them one by one.</p><h2>&#128073; Schema</h2><p>The schema defines what your GraphQL API supports.</p><p>For example:</p><pre><code><code>type Product {
    id: ID!
    name: String!
    price: Float!
}</code></code></pre><p>This tells GraphQL that a <code>Product</code> has:</p><pre><code><code>id
name
price</code></code></pre><p>The schema is effectively the contract between the frontend and backend.</p><p>If a client requests a field that doesn&#8217;t exist:</p><pre><code><code>query {
    product(id: 1) {
        id
        unknownField
    }
}</code></code></pre><p>GraphQL can reject the query because <code>unknownField</code> isn&#8217;t part of the schema.</p><h2>&#128073; GraphQL Types</h2><p>A type describes the structure of an object.</p><p>For example:</p><pre><code><code>type User {
    id: ID!
    name: String!
    email: String!
}</code></code></pre><p>Here:</p><pre><code><code>User</code></code></pre><p>is a GraphQL object type.</p><p>It contains three fields:</p><pre><code><code>id
name
email</code></code></pre><h2>&#128073; GraphQL Scalar Types</h2><p>GraphQL provides several built-in scalar types.</p><pre><code><code>String
Int
Float
Boolean
ID</code></code></pre><p>Example:</p><pre><code><code>type Product {
    id: ID!
    name: String!
    quantity: Int!
    price: Float!
    isActive: Boolean!
}</code></code></pre><p>The exclamation mark is important.</p><pre><code><code>name: String!</code></code></pre><p>means:</p><blockquote><p><code>name</code> cannot be null.</p></blockquote><p>While:</p><pre><code><code>description: String</code></code></pre><p>means:</p><blockquote><p><code>description</code> may be null.</p></blockquote><h2>&#128073; Query</h2><p>A query is used to read data.</p><p>For example:</p><pre><code><code>query {
    products {
        id
        name
        price
    }
}</code></code></pre><p>Conceptually:</p><pre><code><code>GraphQL Query = REST GET</code></code></pre><p>But GraphQL gives the client much more control over the requested response shape.</p><h2>&#128073; Mutation</h2><p>A mutation is used to modify data.</p><p>Typical mutation operations include:</p><pre><code><code>Create
Update
Delete</code></code></pre><p>For example:</p><pre><code><code>mutation {
    createProduct(
        name: "MacBook Pro"
        price: 1999
    ) {
        id
        name
        price
    }
}</code></code></pre><p>Conceptually:</p><pre><code><code>Query     &#8594; Read
Mutation  &#8594; Write</code></code></pre><h2>&#128073; Arguments</h2><p>Fields can accept arguments.</p><p>For example:</p><pre><code><code>type Query {
    product(id: ID!): Product
}</code></code></pre><p>The client can call:</p><pre><code><code>query {
    product(id: 10) {
        id
        name
        price
    }
}</code></code></pre><p>Here:</p><pre><code><code>id</code></code></pre><p>is an argument.</p><h2>&#128073; Input Types</h2><h4>When mutations become more complex, use input types.</h4><p>Instead of:</p><pre><code><code>createProduct(
    name: String!
    description: String
    price: Float!
)</code></code></pre><p>define:</p><pre><code><code>input CreateProductInput {
    name: String!
    description: String
    price: Float!
}</code></code></pre><p>Then:</p><pre><code><code>type Mutation {
    createProduct(
        input: CreateProductInput!
    ): Product!
}</code></code></pre><p>The client can send:</p><pre><code><code>mutation {
    createProduct(
        input: {
            name: "MacBook Pro"
            description: "Apple laptop"
            price: 1999
        }
    ) {
        id
        name
        price
    }
}</code></code></pre><p>Input types become especially useful as mutations evolve.</p><h2>&#128073; Resolver</h2><p><strong>Resolver</strong> is the piece of code that tells GraphQL:</p><blockquote><p><strong>&#8220;When the client asks for this field, where should I get the data from?&#8221;</strong></p></blockquote><p>Think of a resolver as a <strong>bridge between the GraphQL query and your actual application/database</strong>.</p><h3>Simple example</h3><p>Suppose the client sends:</p><pre><code><code>query {
    user(id: 10) {
        id
        name
        email
    }
}</code></code></pre><p>GraphQL knows that there is a <code>user</code> field, but it doesn&#8217;t automatically know how to find user <code>10</code>.</p><p>The <strong>resolver</strong> handles that:</p><pre><code><code>public function user($root, array $args)
{
    return User::find($args['id']);
}</code></code></pre><h3>An important point</h3><p>You don&#8217;t necessarily need to write a resolver for <strong>every field</strong>.</p><p>If Laravel&#8217;s Eloquent relationship already provides the data, your GraphQL library may automatically resolve it.</p><p>For example:</p><pre><code><code>class Order extends Model
{
    public function customer()
    {
        return $this-&gt;belongsTo(Customer::class);
    }

    public function items()
    {
        return $this-&gt;hasMany(OrderItem::class);
    }
}</code></code></pre><p>GraphQL can use these relationships to resolve:</p><pre><code><code>order {
    customer {
        name
    }

    items {
        quantity
    }
}</code></code></pre><h2>&#128073; Enum</h2><p>An <strong>Enum (Enumeration)</strong> is a GraphQL type that defines a <strong>fixed list of allowed values</strong>.</p><p>In simple words:</p><blockquote><p><strong>Enum means: &#8220;You can choose only from these predefined values.&#8221;</strong></p></blockquote><p>Suppose an order can have only these statuses:</p><pre><code><code>pending
processing
shipped
delivered
cancelled</code></code></pre><p>Instead of allowing the client to send any random string:</p><pre><code><code>status: "hello"
status: "something"
status: "abc"</code></code></pre><p>we define an Enum:</p><pre><code><code>enum OrderStatus {
    PENDING
    PROCESSING
    SHIPPED
    DELIVERED
    CANCELLED
}</code></code></pre><p>Now GraphQL knows:</p><blockquote><p><code>OrderStatus</code> can contain <strong>only these five values</strong>.</p></blockquote><h2>&#128073; Interface</h2><p>A <strong>GraphQL Interface</strong> is a way to define a <strong>common set of fields that multiple types must have</strong>.</p><p>In simple words:</p><blockquote><p><strong>Interface = a common contract shared by multiple GraphQL types.</strong></p></blockquote><p>If different types have some common fields, instead of repeating those fields, you can define them once in an interface.</p><p>We can create an interface:</p><pre><code>interface User {
    id: ID!
    name: String!
    email: String!
}</code></pre><p>Then tell GraphQL that these types implement the interface:</p><pre><code>type Customer implements User {
    id: ID!
    name: String!
    email: String!
    orders: [Order!]!
}

type Admin implements User {
    id: ID!
    name: String!
    email: String!
    permissions: [String!]!
}

type Seller implements User {
    id: ID!
    name: String!
    email: String!
    products: [Product!]!
}</code></pre><p>Now <code>Customer</code>, <code>Admin</code>, and <code>Seller</code> <strong>must provide</strong>:</p><pre><code><code>id
name
email</code></code></pre><p>because those fields are defined by the <code>User</code> interface.</p><h1>&#9881;&#65039; Setting Up GraphQL in Laravel</h1><p>Laravel doesn&#8217;t provide GraphQL functionality in the same way it provides Eloquent or routing, so you normally install a GraphQL package.</p><p>A popular Laravel option is <strong>Lighthouse</strong>.</p><p>Install it with Composer:</p><pre><code><code>composer require nuwave/lighthouse</code></code></pre><p>Then publish its configuration:</p><pre><code><code>php artisan vendor:publish --tag=lighthouse-config</code></code></pre><p>You should then have:</p><pre><code><code>config/
&#9492;&#9472;&#9472; lighthouse.php</code></code></pre><h2>&#11088; GraphQL Best Practices</h2><p>Design Your Schema Around the Business Domain</p><p>Keep Resolvers Thin</p><p>Use Input Types for Mutations</p><p>Use Enums for Fixed Values</p><p>Validate Mutation Input</p><h2>&#128204; Conclusion</h2><p>GraphQL provides a flexible and efficient way to build modern APIs by allowing clients to request exactly the data they need.<br>When combined with Laravel, it works well with Eloquent, authentication, validation, and Laravel's existing application architecture.<br>Understanding Queries, Mutations, Resolvers, Types, Enums, Interfaces, and Subscriptions gives you a strong foundation for GraphQL development.<br>With proper schema design, authorization, pagination, and performance optimization, you can build scalable and maintainable GraphQL APIs.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 Laravel Pipeline Pattern: Turn Complex Business Logic Into Clean, Composable Workflows]]></title><description><![CDATA[A practical deep dive into Laravel Pipelines, $next(), failure handling, transactions, compensation, idempotency, and production trade-offs explained through one real-world order-processing workflow]]></description><link>https://newsletter.priteshbhanushali.com/p/laravel-pipeline-pattern-turn-complex</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/laravel-pipeline-pattern-turn-complex</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 23 Aug 2026 04:31:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fOZU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Modern applications rarely perform a single operation.</p><p>A seemingly simple <strong>&#8220;Place Order&#8221;</strong> action might actually involve:</p><ul><li><p>&#128269; Validating the order</p></li><li><p>&#128176; Calculating discounts</p></li><li><p>&#129518; Calculating tax</p></li><li><p>&#128230; Reserving inventory</p></li><li><p>&#128179; Charging the customer</p></li><li><p>&#128190; Persisting the order</p></li><li><p>&#128226; Publishing events</p></li><li><p>&#128231; Sending notifications</p></li></ul><p>When all of this logic lives inside one service method, the code can quickly become difficult to understand and maintain.</p><p>That&#8217;s where the <strong>Pipeline Design Pattern</strong> can help.</p><p>Laravel already provides an excellent implementation through:</p><pre><code><code>Illuminate\Pipeline\Pipeline</code></code></pre><p>In this article, we&#8217;ll use <strong>one example throughout the entire article: an e-commerce order-processing workflow</strong>.</p><div><hr></div><h2>&#129513; What Is the Pipeline Design Pattern?</h2><p>The Pipeline Design Pattern allows us to divide a large workflow into a sequence of smaller processing steps.</p><p>Each step is called a <strong>pipe</strong>.</p><p>Imagine our order-processing workflow:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fOZU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fOZU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 424w, https://substackcdn.com/image/fetch/$s_!fOZU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 848w, https://substackcdn.com/image/fetch/$s_!fOZU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 1272w, https://substackcdn.com/image/fetch/$s_!fOZU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!fOZU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png" width="1064" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1064,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fOZU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 424w, https://substackcdn.com/image/fetch/$s_!fOZU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 848w, https://substackcdn.com/image/fetch/$s_!fOZU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 1272w, https://substackcdn.com/image/fetch/$s_!fOZU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb51d39e-5e22-4a25-8cb6-e7d094e8b975_1064x668.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Instead of one large method containing everything, each operation gets its own class.</p><p>The fundamental idea is:</p><blockquote><p><strong>A pipeline passes a payload through a sequence of processing steps, where each step performs one responsibility and passes the payload to the next step.</strong></p></blockquote><p>This gives us three important properties:</p><ul><li><p>&#127919; <strong>Separation of concerns</strong></p></li><li><p>&#129514; <strong>Better testability</strong></p></li><li><p>&#129521; <strong>Composable workflows</strong></p></li></ul><div><hr></div><h2>&#128560; The Problem: A Growing Service Class</h2><h3>Let&#8217;s start with our order-processing example.</h3><p>A traditional implementation might look something like this:</p><pre><code><code>public function processOrder(Order $order)
{
    $this-&gt;validateOrder($order);

    $discount = $this-&gt;calculateDiscount($order);

    $tax = $this-&gt;calculateTax($order, $discount);

    $this-&gt;inventoryService-&gt;reserve($order);

    $paymentId = $this-&gt;paymentService-&gt;charge($order);

    $this-&gt;orderRepository-&gt;save(
        $order,
        $discount,
        $tax,
        $paymentId
    );
}</code></code></pre><p>At first, this isn&#8217;t bad.</p><p>But requirements rarely stay simple.</p><p>Soon we might need:</p><pre><code><code>Validate order
Validate customer
Apply coupon
Calculate discount
Calculate tax
Check inventory
Reserve inventory
Check payment limit
Charge payment
Create invoice
Persist order
Publish event
Send notification</code></code></pre><p>Our service starts growing.</p><p>Eventually:</p><pre><code><code>public function processOrder(...)
{
    // 300+ lines of business logic
}</code></code></pre><p>Now we have a problem.</p><p>Adding one new business rule requires modifying the same large service.</p><p>Testing one operation may require setting up the entire workflow.</p><p>Understanding the workflow requires reading a large amount of code.</p><p>This is a good candidate for a Pipeline.</p><div><hr></div><h2>&#127959;&#65039; Introducing Laravel Pipeline</h2><p>Laravel gives us:</p><pre><code><code>use Illuminate\Pipeline\Pipeline;</code></code></pre><p>We can define our workflow like this:</p><pre><code><code>app(Pipeline::class)
    -&gt;send($context)
    -&gt;through([
        ValidateOrder::class,
        CalculateDiscount::class,
        CalculateTax::class,
        ReserveInventory::class,
        ChargePayment::class,
        PersistOrder::class,
    ])
    -&gt;thenReturn();</code></code></pre><p>At first glance, this looks simple.</p><p>But there are three important methods here.</p><h3>&#128230; <code>send()</code></h3><pre><code><code>-&gt;send($context)</code></code></pre><p>This defines the object that enters the pipeline.</p><p>In our example, that object represents the current order-processing state.</p><h3>&#128279; <code>through()</code></h3><pre><code><code>-&gt;through([
    ValidateOrder::class,
    CalculateDiscount::class,
    CalculateTax::class,
    ReserveInventory::class,
    ChargePayment::class,
    PersistOrder::class,
])</code></code></pre><p>This defines the sequence of pipes.</p><p>The order matters.</p><p>For example:</p><pre><code><code>Validate
   &#8595;
Discount
   &#8595;
Tax
   &#8595;
Inventory
   &#8595;
Payment
   &#8595;
Persist</code></code></pre><p>is different from:</p><pre><code><code>Payment
   &#8595;
Validate
   &#8595;
Persist</code></code></pre><p>The pipeline makes this order explicit.</p><h3>&#127937; <code>thenReturn()</code></h3><p><code>-&gt;thenReturn();</code></p><p>This executes the pipeline and returns the final payload.</p><p>Laravel also provides <code>then()</code> when you want to define a final destination manually.</p><div><hr></div><h2>&#128230; What Should We Pass Through the Pipeline?</h2><p>Our order workflow needs to carry information between pipes.</p><p>For example:</p><pre><code><code>Order
Discount
Tax
Inventory status
Payment ID
Payment status
Metadata</code></code></pre><p>We could pass these around individually, but that quickly becomes messy.</p><p>Instead, create an <code>OrderContext</code>.</p><pre><code><code>final class OrderContext
{
    public function __construct(
        public Order $order,
        public float $discount = 0,
        public float $tax = 0,
        public bool $inventoryReserved = false,
        public bool $paymentCharged = false,
        public ?string $paymentId = null,
    ) {}
}</code></code></pre><p>Now every pipe receives the same object:</p><pre><code><code>public function handle(
    OrderContext $context,
    Closure $next
): OrderContext</code></code></pre><p>This gives the pipeline a clear contract.</p><div><hr></div><h2>&#128260; How Does a Pipe Work?</h2><p>A typical Laravel pipe looks like this:</p><pre><code><code>class CalculateDiscount
{
    public function handle(
        OrderContext $context,
        Closure $next
    ): OrderContext {

        // Process current step

        return $next($context);
    }
}</code></code></pre><p>There are two important things here:</p><pre><code><code>$context</code></code></pre><p>and:</p><pre><code><code>$next</code></code></pre><p>The context is our data.</p><p>The <code>$next</code> callback represents the remaining pipeline.</p><p>When we write:</p><pre><code><code>return $next($context);</code></code></pre><p>we are saying:</p><blockquote><p><strong>&#8220;My work is complete. Pass this context to the next pipe.&#8221;</strong></p></blockquote><p>This is the most important line to understand when learning Laravel Pipeline.</p><div><hr></div><h2>&#128736;&#65039; Building Our Order Pipeline</h2><p>Now let&#8217;s implement our workflow.</p><p>Our pipeline will contain six pipes:</p><pre><code><code>1&#65039;&#8419; ValidateOrder
2&#65039;&#8419; CalculateDiscount
3&#65039;&#8419; CalculateTax
4&#65039;&#8419; ReserveInventory
5&#65039;&#8419; ChargePayment
6&#65039;&#8419; PersistOrder</code></code></pre><p>The final configuration becomes:</p><pre><code><code>app(Pipeline::class)
    -&gt;send($context)
    -&gt;through([
        ValidateOrder::class,
        CalculateDiscount::class,
        CalculateTax::class,
        ReserveInventory::class,
        ChargePayment::class,
        PersistOrder::class,
    ])
    -&gt;thenReturn();</code></code></pre><p>Notice how easy the workflow is to read.</p><p>A developer can understand the high-level business process without reading every implementation.</p><p>That&#8217;s one of the biggest advantages of Pipeline.</p><h3>&#128269; Pipe #1: Validate Order</h3><p>The first pipe validates the order.</p><pre><code><code>class ValidateOrder
{
    public function handle(
        OrderContext $context,
        Closure $next
    ): OrderContext {

        if ($context-&gt;order-&gt;items-&gt;isEmpty()) {
            throw new ValidationException(
                'Order must contain at least one item.'
            );
        }

        return $next($context);
    }
}</code></code></pre><p>This pipe has one job:</p><blockquote><p>Validate the order.</p></blockquote><p>It doesn&#8217;t calculate tax.</p><p>It doesn&#8217;t charge payment.</p><p>It doesn&#8217;t reserve inventory.</p><p>That separation is intentional.</p><h3>&#127991;&#65039; Pipe #2: Calculate Discount</h3><p>Suppose our business rule is:</p><pre><code><code>Order &gt;= $1,000 &#8594; 10% discount
Order &gt;= $500   &#8594; 5% discount
Otherwise       &#8594; No discount</code></code></pre><p>The pipe can implement that rule:</p><pre><code><code>class CalculateDiscount
{
    public function handle(
        OrderContext $context,
        Closure $next
    ): OrderContext {

        $subtotal = $context-&gt;order-&gt;subtotal;

        if ($subtotal &gt;= 1000) {
            $context-&gt;discount = $subtotal * 0.10;
        } elseif ($subtotal &gt;= 500) {
            $context-&gt;discount = $subtotal * 0.05;
        }

        return $next($context);
    }
}</code></code></pre><p>Again, one responsibility.</p><h3>&#129518; Pipe #3: Calculate Tax</h3><p>Now the tax pipe uses the values calculated by the previous pipe.</p><pre><code><code>class CalculateTax
{
    public function handle(
        OrderContext $context,
        Closure $next
    ): OrderContext {

        $taxableAmount =
            $context-&gt;order-&gt;subtotal
            - $context-&gt;discount;

        $context-&gt;tax = $taxableAmount * 0.18;

        return $next($context);
    }
}</code></code></pre><p>Notice something important.</p><p>The pipeline allows one pipe to prepare information for the next pipe.</p><pre><code><code>Discount Pipe
      &#9474;
      &#9660;
context-&gt;discount
      &#9474;
      &#9660;
Tax Pipe</code></code></pre><p>This is why the shared context is useful.</p><h3>&#128230; Pipe #4: Reserve Inventory</h3><p>Now we have a side effect.</p><p>The inventory service reserves the products.</p><pre><code><code>class ReserveInventory
{
    public function __construct(
        private InventoryService $inventory
    ) {}

    public function handle(
        OrderContext $context,
        Closure $next
    ): OrderContext {

        $this-&gt;inventory-&gt;reserve(
            $context-&gt;order
        );

        $context-&gt;inventoryReserved = true;

        return $next($context);
    }
}</code></code></pre><p>Laravel&#8217;s service container resolves <code>InventoryService</code> automatically.</p><p>This is another advantage of using Laravel Pipeline rather than manually creating every pipe.</p><h3>&#128179; Pipe #5: Charge Payment</h3><p>Now we charge the customer.</p><pre><code><code>class ChargePayment
{
    public function __construct(
        private PaymentService $payment
    ) {}

    public function handle(
        OrderContext $context,
        Closure $next
    ): OrderContext {

        $paymentId = $this-&gt;payment-&gt;charge(
            $context-&gt;order-&gt;id,
            $context-&gt;order-&gt;total
        );

        $context-&gt;paymentId = $paymentId;
        $context-&gt;paymentCharged = true;

        return $next($context);
    }
}</code></code></pre><p>This operation is different from calculating tax.</p><p>It interacts with an external system.</p><p>That difference becomes very important when we discuss failure handling.</p><h3>&#128190; Pipe #6: Persist the Order</h3><p>Finally, we save the order.</p><pre><code><code>class PersistOrder
{
    public function handle(
        OrderContext $context,
        Closure $next
    ): OrderContext {

        $context-&gt;order-&gt;update([
            'discount' =&gt; $context-&gt;discount,
            'tax' =&gt; $context-&gt;tax,
            'total' =&gt;
                $context-&gt;order-&gt;subtotal
                - $context-&gt;discount
                + $context-&gt;tax,
            'payment_id' =&gt; $context-&gt;paymentId,
            'status' =&gt; 'completed',
        ]);

        return $next($context);
    }
}</code></code></pre><div><hr></div><h2>&#10060; What Happens When a Pipe Fails?</h2><p>Now let&#8217;s look at the most important production question:</p><blockquote><p>What happens when something goes wrong?</p></blockquote><p>Suppose payment fails:</p><pre><code><code>&#128269; Validate       &#9989;
&#127991;&#65039; Discount       &#9989;
&#129518; Tax            &#9989;
&#128230; Inventory      &#9989;
&#128179; Payment        &#10060;
&#128190; Persist        &#9940;</code></code></pre><p>The payment pipe throws an exception:</p><pre><code><code>throw new PaymentException(
    'Payment provider rejected the payment.'
);</code></code></pre><p>The pipeline stops normal forward execution.</p><p><code>PersistOrder</code> doesn&#8217;t execute.</p><p>This behavior is useful because later operations don&#8217;t run after a failure.</p><p>But we now have another problem.</p><p>Inventory was already reserved.</p><div><hr></div><h3>&#9888;&#65039; Pipeline Does NOT Automatically Roll Back</h3><p>This is one of the biggest misconceptions about Pipeline.</p><p>You might think:</p><blockquote><p>&#8220;If payment fails, Laravel will automatically undo inventory reservation.&#8221;</p></blockquote><p>It won&#8217;t.</p><p>Pipeline controls <strong>execution flow</strong>.</p><p>It doesn&#8217;t know how to reverse your business operations.</p><p>So we have:</p><pre><code><code>&#128230; Inventory Reserved
        &#8595;
&#128179; Payment Failed
        &#8595;
&#10067; Inventory is still reserved</code></code></pre><p>We need an explicit strategy.</p><div><hr></div><h2>&#127919; Best Practices</h2><h3>1. Give Every Pipe One Responsibility</h3><p>Good:</p><pre><code><code>ValidateOrder
CalculateDiscount
CalculateTax</code></code></pre><p>Avoid:</p><pre><code><code>ProcessEntireOrder</code></code></pre><h3>2. Keep the Pipeline Declaration Easy to Read</h3><p>This is good:</p><pre><code><code>-&gt;through([
    ValidateOrder::class,
    CalculateDiscount::class,
    CalculateTax::class,
    ReserveInventory::class,
    ChargePayment::class,
    PersistOrder::class,
])</code></code></pre><p>The code itself documents the workflow.</p><h3>3. Keep Side Effects Explicit</h3><p>A pipe called:</p><pre><code><code>CalculateTax</code></code></pre><p>shouldn&#8217;t secretly charge a payment.</p><p>Predictability is important.</p><h3>4. Make Failure Behavior Explicit</h3><h4>Every pipe should have a clear answer to:</h4><blockquote><p>What happens if this operation fails?</p></blockquote><p>For example:</p><pre><code><code>Validation
   &#8594; Stop

Inventory
   &#8594; Stop

Payment
   &#8594; Stop + release inventory

Persistence
   &#8594; Stop + refund payment + release inventory</code></code></pre><h3>5. Don&#8217;t Make Everything a Pipe</h3><p>This is important.</p><p>If a method contains two simple operations and is already easy to understand, introducing five pipe classes may make the design worse.</p><p>Use abstraction when it provides value.</p><h3>6. Keep External Side Effects in Mind</h3><p>Before introducing:</p><pre><code><code>Payment
Email
External API
Message Queue</code></code></pre><p>ask:</p><blockquote><p>&#8220;How will I handle this if a later step fails?&#8221;</p></blockquote><p>That question can prevent many production problems.</p><div><hr></div><h2>&#9878;&#65039; Pipeline Trade-offs</h2><p>Pipeline has significant benefits.</p><h4>&#9989; Separation of Concerns</h4><p>Each pipe focuses on one task.</p><h4>&#9989; Readability</h4><p>The workflow becomes explicit.</p><h4>&#9989; Testability</h4><p>Pipes can be tested independently.</p><h4>&#9989; Extensibility</h4><p>New steps can be added without turning one service into a giant method.</p><h4>&#9989; Reusability</h4><p>Pipes can potentially be reused across workflows.</p><p>But there are trade-offs.</p><h4>&#10060; More Classes</h4><p>A six-step workflow may require six pipe classes.</p><h4>&#10060; More Indirection</h4><p>Developers need to jump between classes to understand the implementation.</p><h4>&#10060; Shared State Can Become Complicated</h4><p>A large context object can eventually become difficult to manage.</p><h4>&#10060; Rollback Isn&#8217;t Automatic</h4><p>Pipeline doesn&#8217;t provide transaction semantics.</p><h4>&#10060; Distributed Workflows Need More Architecture</h4><div><hr></div><h2>&#128678; When Should You Use Pipeline?</h2><p>Pipeline is a good fit when:</p><h4>&#128313; You have a sequential workflow</h4><pre><code><code>A &#8594; B &#8594; C &#8594; D</code></code></pre><h4>&#128313; Each step has a clear responsibility</h4><pre><code><code>Validate
Transform
Calculate
Persist</code></code></pre><h4>&#128313; The workflow changes frequently</h4><p>Pipes can be added, removed, or reordered.</p><h4>&#128313; Steps need to be tested independently</h4><p>Each pipe can have focused tests.</p><h4>&#128313; The processing sequence is important</h4><p>The pipeline makes the sequence explicit.</p><div><hr></div><h2>&#128721; When Should You NOT Use Pipeline?</h2><p>Don&#8217;t use Pipeline just because Laravel provides it.</p><p>For example, this would be unnecessary:</p><pre><code><code>app(Pipeline::class)
    -&gt;send($user)
    -&gt;through([
        LoadUser::class,
        UpdateUser::class,
    ])
    -&gt;thenReturn();</code></code></pre><p>if all you really need is:</p><pre><code><code>$user-&gt;update([
    'name' =&gt; $name,
]);</code></code></pre><p>The goal of a design pattern is not to make code more sophisticated.</p><p>The goal is to make the system <strong>easier to understand, change, and maintain</strong>.</p><h2>&#127937; Final Conclusion</h2><p>The Laravel Pipeline Pattern is much more than a convenient way to divide a large method into several smaller classes. Its real value is in <strong>making the flow of a business process explicit, predictable, and easier to evolve</strong>.</p><p>The Pipeline Design Pattern isn&#8217;t about creating more classes.</p><p>It&#8217;s about <strong>giving a complex workflow a clear structure</strong>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🔗 Dependency Injection Explained: From Tight Coupling to Clean Architecture]]></title><description><![CDATA[Dependency Injection in Laravel: Build Flexible, Testable Applications with the Service Container]]></description><link>https://newsletter.priteshbhanushali.com/p/dependency-injection-explained-from</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/dependency-injection-explained-from</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 15 Aug 2026 09:31:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/26f1d8a3-a51e-4867-9071-f51ed4a6175a_1254x1254.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>&#128137; Introduction</h2><p>Imagine you&#8217;re running a restaurant.</p><p>Your chef shouldn&#8217;t have to leave the kitchen every time they need a fresh ingredient. They shouldn&#8217;t care whether the ingredient comes from Supplier A, Supplier B, or a local farmer.</p><p>They simply say:</p><blockquote><p>&#8220;I need tomatoes.&#8221;</p></blockquote><p>The restaurant&#8217;s supply system figures out where those tomatoes should come from.</p><h4>Dependency Injection works the same way.</h4><p>Instead of a class creating everything it needs by itself, you give it the dependencies it needs from the outside.</p><p>In Laravel, the <strong>Service Container</strong> takes this idea even further. It can automatically figure out which classes your application needs and resolve them for you.</p><p>This becomes especially useful when working with external API clients, payment gateways, repositories, database services, and other application services.</p><p>For example, this controller looks innocent:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;33fc5da5-af8b-4fbc-ade8-3cb8d71acae6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class PaymentController extends Controller
{
    public function pay()
    {
        $paymentService = new StripePaymentService();
        return $paymentService-&gt;charge();
    }
}</code></pre></div><p>But now the controller is tightly coupled to <code>StripePaymentService</code>.</p><p>Want to switch to PayPal?</p><p>You have to modify the controller.</p><p>Want to mock the payment service in a test?</p><p>You have another problem.</p><p>Want to reuse the controller with a different implementation?</p><p>Things get messy quickly.</p><p>That&#8217;s where <strong>Dependency Injection (DI)</strong> comes in.</p><p>In simple terms:</p><blockquote><p><strong>Dependency Injection means giving a class the objects it needs instead of making the class create those objects itself.</strong></p></blockquote><p>And Laravel&#8217;s <strong>Service Container</strong> does most of the heavy lifting for you.</p><div><hr></div><h2>&#10060; The Problem: Without DI and Tight Coupling</h2><p>Let&#8217;s start with a typical Laravel payment controller.</p><p>Suppose we have this service:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;d3337923-baac-48bc-8822-2d363dea4c99&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">namespace App\Services;
class StripePaymentService
{
    public function charge(): string
    {
        return "Payment processed through Stripe.";
    }
}</code></pre></div><p>Now our controller directly creates that service:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;1bf90304-97b4-49b9-b9cc-f3e3b89bd04e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">namespace App\Http\Controllers;
use App\Services\StripePaymentService;
class PaymentController extends Controller
{
    public function pay()
    {
        $paymentService = new StripePaymentService();
        return $paymentService-&gt;charge();
    }
}</code></pre></div><p>At first glance, this works perfectly.</p><p>So what&#8217;s the problem?</p><p>The controller knows <strong>too much</strong>.</p><p>It knows that:</p><ul><li><p>Stripe is being used.</p></li><li><p><code>StripePaymentService</code> is the implementation.</p></li><li><p>The service must be created using <code>new</code>.</p></li><li><p>Stripe is the payment provider.</p></li></ul><p>The controller is now tightly coupled to a concrete implementation.</p><h2>&#128073; Problem #1: Testing Becomes Difficult</h2><p>Imagine writing a PHPUnit or Pest test.</p><p>You don&#8217;t actually want to call Stripe while running your unit tests.</p><p>You want to replace the real payment service with a mock.</p><p>But the controller does this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;84a52d92-d59d-48e4-b91c-9a72d1655141&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">$paymentService = new StripePaymentService();</code></pre></div><p>The controller creates the dependency itself.</p><p>That makes replacing it much harder.</p><p>You can&#8217;t simply tell the controller:</p><pre><code><code>"Use this mock instead."</code></code></pre><p>because the controller has already decided which concrete class to instantiate.</p><div><hr></div><h2>&#128073; Problem #2: Switching Payment Providers</h2><p>Suppose your business decides to move from Stripe to PayPal.</p><p>You might end up changing:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;a83bafa8-e4ff-4870-b8ba-81ab41951468&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">$paymentService = new StripePaymentService();</code></pre></div><p>to:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;bea3a8f2-5a85-4f8c-b23e-c468776735d7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">$paymentService = new PayPalPaymentService();</code></pre></div><p>Now the controller contains payment-provider-specific logic.</p><p>And imagine having this dependency in ten different controllers.</p><p>You now have ten places to update.</p><p>That&#8217;s a maintenance problem.</p><div><hr></div><h2>&#128073; Problem #3: Controllers Become Responsible for Object Creation</h2><p>A controller should primarily coordinate application behavior.</p><p>It shouldn&#8217;t be responsible for figuring out how every dependency should be constructed.</p><p>This:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;2aa11150-408c-472d-8900-39aa411a320a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">public function pay()
{
    $paymentService = new StripePaymentService();

    return $paymentService-&gt;charge();
}</code></pre></div><p>mixes two responsibilities:</p><ol><li><p>Creating the dependency.</p></li><li><p>Using the dependency.</p></li></ol><p>DI separates those responsibilities.</p><div><hr></div><h2>&#9989; The Solution: Dependency Injection</h2><p>Let&#8217;s refactor the same controller.</p><p>Instead of creating <code>StripePaymentService</code> ourselves, we inject it through the constructor.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;58a007fb-35cd-4a4e-b877-968acb8bf304&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">namespace App\Http\Controllers;
use App\Services\StripePaymentService;
class PaymentController extends Controller
{
    public function __construct(private StripePaymentService $paymentService) {}
    public function pay()
    {
        return $this-&gt;paymentService-&gt;charge();
    }
}</code></pre></div><p>Notice what&#8217;s gone:</p><pre><code><code>new StripePaymentService();</code></code></pre><p>The controller no longer creates the service.</p><p>Instead, we tell Laravel:</p><blockquote><p>&#8220;This controller needs a <code>StripePaymentService</code>.&#8221;</p></blockquote><p>Laravel figures out how to provide it.</p><p>That&#8217;s <strong>Constructor Injection</strong>.</p><div><hr></div><h2>&#127919; How Laravel Automatically Resolves Dependencies</h2><p>This is where Laravel&#8217;s <strong>Service Container</strong> becomes powerful.</p><p>When Laravel needs to create:</p><pre><code><code>PaymentController</code></code></pre><p>it sees:</p><pre><code>private StripePaymentService $paymentService</code></pre><p>Laravel inspects the constructor and understands:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uIGb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uIGb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uIGb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uIGb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uIGb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!uIGb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:394485,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/211265143?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uIGb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!uIGb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!uIGb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!uIGb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd29ff84a-bc19-41eb-9e5e-c2a7489f7060_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>If <code>StripePaymentService</code> doesn&#8217;t have any complicated dependencies, Laravel can automatically instantiate it.</p><p>This is called <strong>automatic resolution</strong> or <strong>auto-wiring</strong>.</p><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;411ed2d8-31c6-40e7-8066-742f32d24ec8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class StripePaymentService
{
    public function charge(): string
    {
        return "Payment processed through Stripe.";
    }
}</code></pre></div><p>Laravel can resolve this automatically because it is a concrete class that doesn&#8217;t require any unresolved dependencies.</p><p>So you don&#8217;t need to manually write:</p><pre><code><code>app()-&gt;make(StripePaymentService::class);</code></code></pre><p>and you don&#8217;t need:</p><pre><code><code>new StripePaymentService();</code></code></pre><p>Laravel&#8217;s container handles it.</p><div><hr></div><h2>&#9878;&#65039; Constructor Injection vs. Manual Instantiation</h2><p>Without DI:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;ea0f4e92-2d10-4828-b0e4-0d3145d5f34d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class PaymentController extends Controller
{
    public function pay()
    {
        $service = new StripePaymentService();
        return $service-&gt;charge();
    }
}</code></pre></div><p>With DI:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;b3fef60c-300b-4bd1-81ba-792782645a11&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class PaymentController extends Controller
{
    public function __construct(private StripePaymentService $service) {}
    public function pay()
    {
        return $this-&gt;service-&gt;charge();
    }
}</code></pre></div><p>The second version is cleaner because the controller doesn&#8217;t care how the service is created.</p><p>It only cares that it has a service it can use.</p><div><hr></div><h2>&#128268; Dependency Injection with Interfaces</h2><p>Constructor injection is already useful.</p><p>But we can make our application even more flexible.</p><p>Instead of making the controller depend on:</p><pre><code><code>StripePaymentService</code></code></pre><p>we can make it depend on an interface:</p><pre><code><code>PaymentGatewayInterface</code></code></pre><p>This is where DI becomes really powerful.</p><p>Let&#8217;s create the interface.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;8806723b-058a-4393-aa38-93cf20df27a1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">namespace App\Contracts;

interface PaymentGatewayInterface
{
    public function charge(): string;
}</code></pre></div><p>Now Stripe implements that interface:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;c2469a34-2ce7-4545-9eed-4a4450c968e0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">namespace App\Services;
use App\Contracts\PaymentGatewayInterface;
class StripePaymentService implements PaymentGatewayInterface
{
    public function charge(): string
    {
        return "Payment processed through Stripe.";
    }
}</code></pre></div><p>Our controller no longer needs to know about Stripe.</p><p>It only knows about the contract.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;2fd17afa-8d70-4e4d-a3e7-4cda63ee6de7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">namespace App\Http\Controllers;

use App\Contracts\PaymentGatewayInterface;

class PaymentController extends Controller
{
    public function __construct(
        private PaymentGatewayInterface $paymentGateway
    ) {
    }

    public function pay()
    {
        return $this-&gt;paymentGateway-&gt;charge();
    }
}</code></pre></div><p>But there&#8217;s one problem.</p><p>Laravel knows how to automatically resolve concrete classes.</p><p>It doesn&#8217;t automatically know which implementation should be used for:</p><pre><code><code>PaymentGatewayInterface</code></code></pre><p>There could be several implementations.</p><p>For example:</p><pre><code><code>StripePaymentService
PayPalPaymentService
RazorpayPaymentService</code></code></pre><p>So we need to tell Laravel:</p><blockquote><p>&#8220;Whenever someone asks for <code>PaymentGatewayInterface</code>, give them <code>StripePaymentService</code>.&#8221;</p></blockquote><div><hr></div><h2>&#128295; Binding the Interface to an Implementation</h2><p>The natural place for this binding is a service provider.</p><p>For example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;80ad62f2-51b3-47d7-8e6d-c34df329ee47&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">namespace App\Providers;
use App\Contracts\PaymentGatewayInterface;
use App\Services\StripePaymentService;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this-&gt;app-&gt;bind(
            PaymentGatewayInterface::class,
            StripePaymentService::class,
        );
    }
}</code></pre></div><p>Now Laravel has a mapping:</p><pre><code><code>PaymentGatewayInterface
          &#8595;
StripePaymentService</code></code></pre><p>Whenever Laravel sees:</p><pre><code><code>PaymentGatewayInterface</code></code></pre><p>it knows to resolve:</p><pre><code><code>StripePaymentService</code></code></pre><p>Our controller remains completely unaware of Stripe.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;48517e57-ddaf-4d72-82fe-ab8beff85e45&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class PaymentController extends Controller
{
    public function __construct(
        private PaymentGatewayInterface $paymentGateway
    ) {
    }

    public function pay()
    {
        return $this-&gt;paymentGateway-&gt;charge();
    }
}</code></pre></div><p>This is a major architectural improvement.</p><div><hr></div><h2>&#128260; Switching from Stripe to PayPal</h2><p>Now imagine the business decides to use PayPal.</p><p>Create another implementation:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;b56ec3a3-1ab9-41ed-9f21-df65d5eb289c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">namespace App\Services;
use App\Contracts\PaymentGatewayInterface;
class PayPalPaymentService implements PaymentGatewayInterface
{
    public function charge(): string
    {
        return "Payment processed through PayPal.";
    }
}</code></pre></div><p>Then change the container binding:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;a809795c-140c-47ac-b1bb-4085c70fd2cd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">$this-&gt;app-&gt;bind(
    PaymentGatewayInterface::class,
    PayPalPaymentService::class
);</code></pre></div><p>That&#8217;s it.</p><p>The controller doesn&#8217;t change.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;ebf0ec35-c846-4a4e-8228-fb49efa727d3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class PaymentController extends Controller
{
    public function __construct(
        private PaymentGatewayInterface $paymentGateway,
    ) {}
    public function pay()
    {
        return $this-&gt;paymentGateway-&gt;charge();
    }
}</code></pre></div><p>The controller doesn&#8217;t know whether it&#8217;s using:</p><pre><code><code>Stripe
PayPal
Razorpay
Adyen
MockPaymentGateway</code></code></pre><p>It only knows:</p><pre><code><code>PaymentGatewayInterface</code></code></pre><p>That&#8217;s <strong>true decoupling</strong>.</p><div><hr></div><h2>&#127919; Key Benefits of Dependency Injection in Laravel</h2><h3>1. Painless Unit Testing</h3><p>One of the biggest advantages of DI is testability.</p><p>Because the dependency is injected, you can replace the real implementation with a mock.</p><p>For example, Laravel&#8217;s container can bind a mock:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;0b22d7dc-c681-40c4-a3b5-9a9156b069ef&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">$this-&gt;mock(PaymentGatewayInterface::class, function ($mock) {
    $mock-&gt;shouldReceive('charge')
        -&gt;once()
        -&gt;andReturn('Payment successful.');
});</code></pre></div><p>Now your test doesn&#8217;t need to call a real payment provider.</p><p>You can also use Mockery directly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;0ffb0889-0409-46a2-b51e-27fe14f52a81&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">$gateway = Mockery::mock(PaymentGatewayInterface::class);

$gateway-&gt;shouldReceive('charge')
    -&gt;once()
    -&gt;andReturn('Payment successful.');</code></pre></div><p>This makes unit tests faster, deterministic, and safer.</p><p>You don&#8217;t want your PHPUnit test suite accidentally charging a real credit card.</p><h3>2. True Decoupling and Flexibility</h3><p>Without DI:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;4c121a05-22ce-42f3-9529-327a09b17f23&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class PaymentController
{
    public function pay()
    {
        $service = new StripePaymentService();
    }
}

</code></pre></div><p>The controller depends directly on Stripe.</p><p>With DI:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;b9640fe4-f89f-4f70-962d-bfe584d89bdc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">class PaymentController
{
    public function __construct(
        private PaymentGatewayInterface $paymentGateway
    ) {
    }
}</code></pre></div><p>The controller depends on an abstraction.</p><p>That&#8217;s the important difference.</p><p>You can change the implementation without rewriting the controller.</p><h3>3. Simpler, Cleaner Controllers</h3><p>A controller shouldn&#8217;t be responsible for constructing a dependency graph.</p><p>Avoid this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;ba582c0c-2d50-49cb-818f-566bc707d7b5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">public function pay()
{
    $client = new StripeClient();
    $logger = new PaymentLogger();
    $repository = new PaymentRepository();
    $service = new PaymentService(
        $client,
        $logger,
        $repository
    );

    return $service-&gt;process();
}</code></pre></div><p>Instead, let Laravel resolve the dependency graph:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;9573f4d1-4e09-457c-98f7-cd42de1d5f86&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">public function __construct(
    private PaymentService $paymentService
) {
}</code></pre></div><p>Your controller becomes easier to read.</p><p>And more importantly, it becomes easier to change.</p><div><hr></div><h2>&#128640; Conclusion: Let Laravel Handle the Heavy Lifting</h2><h4>Dependency Injection can sound like a complicated architectural concept.</h4><h4>In practice, the idea is simple:</h4><blockquote><p><strong>Don&#8217;t make a class create the things it needs. Give it those things instead.</strong></p></blockquote><p>Laravel makes this especially powerful through its <strong>Service Container</strong>.</p><p>You can start with simple constructor injection:</p><pre><code><code>public function __construct(
    private PaymentService $paymentService
) {
}</code></code></pre><p>And when you need more flexibility, introduce an interface:</p><pre><code><code>public function __construct(
    private PaymentGatewayInterface $paymentGateway
) {
}</code></code></pre><p>Then bind the implementation:</p><pre><code><code>$this-&gt;app-&gt;bind(
    PaymentGatewayInterface::class,
    StripePaymentService::class
);</code></code></pre><p>Now your controllers don&#8217;t care about concrete implementations.</p><p>They care about contracts.</p><p>That&#8217;s the real value of Dependency Injection.</p><p>It gives you:</p><ul><li><p><strong>Cleaner controllers</strong></p></li><li><p><strong>Easier testing</strong></p></li><li><p><strong>Looser coupling</strong></p></li><li><p><strong>Swappable implementations</strong></p></li><li><p><strong>More maintainable architecture</strong></p></li></ul><p>And the best part?</p><p><strong>Laravel&#8217;s Service Container handles most of the heavy lifting for you.</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🧠 The Right Way to Handle Complex Business Logic in Laravel]]></title><description><![CDATA[Master Laravel Events & Listeners with a Real-World Uber Ride Example]]></description><link>https://newsletter.priteshbhanushali.com/p/the-right-way-to-handle-complex-business</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/the-right-way-to-handle-complex-business</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 25 Jul 2026 04:31:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!wkR8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every Laravel developer eventually reaches a point where a single controller begins to do too much.</p><p>At first, everything feels manageable.</p><p>Your application only needs to save data to the database.</p><p>Later, product requirements evolve.</p><p>Marketing wants an email.</p><p>Finance wants an invoice.</p><p>Operations wants analytics.</p><p>Customer Support wants notifications.</p><p>The Product team wants reward points.</p><p>Engineering wants audit logs.</p><p>Before long, your once-simple controller has become responsible for half of your application&#8217;s business logic.</p><p>If you&#8217;ve ever opened a controller and found hundreds of lines of code performing unrelated tasks, you&#8217;re not alone.</p><p>This is exactly the problem Laravel&#8217;s <strong>Event-Driven Architecture</strong> solves.</p><p>Instead of placing every responsibility inside your controller, Laravel allows you to announce that <strong>something happened</strong>, while independent components react to that event.</p><p>In this article, we&#8217;ll build a complete Uber Ride Booking workflow using Laravel Events and Listeners and learn why this design pattern is used in production applications.</p><div><hr></div><h2>&#128680; The Problem We&#8217;re Trying to Solve</h2><p>Imagine we&#8217;re building an Uber-like ride booking platform.</p><p>When a rider completes a trip, several things need to happen.</p><ul><li><p>Save ride information</p></li><li><p>Charge the customer</p></li><li><p>Send an email receipt</p></li><li><p>Generate an invoice</p></li><li><p>Update the driver&#8217;s earnings</p></li><li><p>Award loyalty points</p></li><li><p>Notify the accounting team</p></li><li><p>Store analytics data</p></li><li><p>Trigger a webhook for third-party integrations</p></li></ul><p>Most developers initially write everything inside the controller.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;6b57fb6f-ae09-49e7-8159-980f2258da3f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">public function completeRide(Request $request)
{
    $ride = Ride::create([
        'driver_id' =&gt; $request-&gt;driver_id,
        'rider_id'  =&gt; $request-&gt;rider_id,
        'fare'      =&gt; $request-&gt;fare,
        'status'    =&gt; 'completed',
    ]);

    PaymentService::charge($ride);

    Mail::to($ride-&gt;rider)-&gt;send(new RideReceiptMail($ride));

    InvoiceService::generate($ride);

    DriverService::updateEarnings($ride);

    RewardPointService::addPoints($ride);

    AnalyticsService::trackRide($ride);

    Notification::send(
        User::accountingTeam(),
        new RideCompletedNotification($ride)
    );

    return response()-&gt;json([
        'message' =&gt; 'Ride Completed'
    ]);
}</code></pre></div><p>At first glance, this code looks perfectly fine.</p><p>Everything works.</p><p>So what&#8217;s wrong?</p><p>The problem isn&#8217;t functionality.</p><p>The problem is <strong>architecture</strong>.</p><div><hr></div><h2>&#9940; Why This Controller Is a Problem</h2><p>Look carefully.</p><p>Our controller now knows about:</p><ul><li><p>Payment Service</p></li><li><p>Mail Service</p></li><li><p>Invoice Service</p></li><li><p>Driver Service</p></li><li><p>Reward Service</p></li><li><p>Analytics</p></li><li><p>Notifications</p></li></ul><p>That&#8217;s <strong>seven different dependencies</strong>.</p><p>Now imagine six months later.</p><p>Management asks for:</p><ul><li><p>Send SMS</p></li><li><p>Push Notification</p></li><li><p>Slack Notification</p></li><li><p>Webhook Integration</p></li><li><p>Fraud Detection</p></li><li><p>AI Recommendation Engine</p></li></ul><p>Guess what happens?</p><p>We open the same controller again.</p><p>And again.</p><p>And again.</p><p>Eventually, the controller grows into something like this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;323de6c4-78b2-4b03-bd31-eb26453a07f1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">RideController

&#8595;

Create Ride

&#8595;

Charge Customer

&#8595;

Send Email

&#8595;

Generate Invoice

&#8595;

Update Driver Wallet

&#8595;

Update Driver Rating

&#8595;

Reward Rider

&#8595;

Send SMS

&#8595;

Push Notification

&#8595;

Slack Notification

&#8595;

Webhook

&#8595;

Analytics

&#8595;

Audit Logs

&#8595;

Fraud Detection</code></pre></div><p>The controller is no longer responsible for one thing.</p><p>It&#8217;s responsible for <strong>everything</strong>.</p><p>This violates the <strong>Single Responsibility Principle (SRP)</strong>.</p><div><hr></div><h2>&#128276; What Is an Event?</h2><p>Think about a fire alarm in a building.</p><p>When someone pulls the alarm:</p><ul><li><p>The alarm doesn&#8217;t call the fire department.</p></li><li><p>It doesn&#8217;t contact security.</p></li><li><p>It doesn&#8217;t evacuate people.</p></li></ul><p>It simply announces:</p><blockquote><p><strong>A fire has occurred.</strong></p></blockquote><p>Different departments react independently.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;0a835dd7-c90f-4bf6-97fd-50a6d9652efe&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Fire Alarm

&#8595;

Fire Department Responds

&#8595;

Security Responds

&#8595;

Medical Team Responds

&#8595;

Building Evacuates</code></pre></div><p>Laravel Events work exactly the same way.</p><p>An Event simply says:</p><blockquote><p>Something important happened.</p></blockquote><p>Nothing more.</p><h2>&#128073; In Our Ride Booking System</h2><p>When a ride finishes successfully, our application doesn&#8217;t need to know who is interested.</p><p>It simply announces:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;cbe8134d-f655-4f37-98a1-89913260b020&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">RideCompleted</code></pre></div><p>That&#8217;s it.</p><div><hr></div><h2>&#128066;What Is a Listener?</h2><p>A Listener waits for an event.</p><p>When the event occurs, it performs exactly one responsibility.</p><p>For our application, we might have these listeners:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;cdc4162b-2404-4aaa-bc5f-681d969ffe2f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">RideCompleted

&#9500;&#9472;&#9472; ChargeCustomer

&#9500;&#9472;&#9472; SendReceiptEmail

&#9500;&#9472;&#9472; GenerateInvoice

&#9500;&#9472;&#9472; UpdateDriverWallet

&#9500;&#9472;&#9472; RewardRider

&#9500;&#9472;&#9472; NotifyAccounting

&#9500;&#9472;&#9472; StoreAnalytics</code></pre></div><p>Notice something interesting.</p><p>Each listener has <strong>one job</strong>.</p><p>Nothing more.</p><p>Nothing less.</p><div><hr></div><h2>&#127919; Understanding Laravel's Event Flow</h2><p>Here&#8217;s what actually happens internally.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wkR8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wkR8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!wkR8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!wkR8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!wkR8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!wkR8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:713791,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/207697291?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wkR8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!wkR8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!wkR8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!wkR8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc153d0e8-5bee-4dbb-b03a-219d4961bb36_1024x559.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The controller has no idea who receives the event.</p><p>Laravel&#8217;s Event Dispatcher takes care of everything.</p><p>This is called <strong>Loose Coupling</strong>.</p><div><hr></div><h2>&#9889; Why Is This Better?</h2><p>Imagine tomorrow the business says:</p><blockquote><p>&#8220;Whenever a ride completes, also notify our CRM system.&#8221;</p></blockquote><p>Without Events:</p><p>You modify the controller.</p><p>Again.</p><p>With Events:</p><p>You simply create another listener.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;792ad25d-9aba-47f6-b832-23b5a367027b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">RideCompleted

&#8595;

SyncWithCRM Listener</code></pre></div><p>No controller changes.</p><p>No service modifications.</p><p>No risk of breaking existing logic.</p><p>This follows the <strong>Open/Closed Principle (OCP)</strong>:</p><blockquote><p>Software should be open for extension but closed for modification.</p></blockquote><div><hr></div><h2>&#9878;&#65039; Event vs Listener</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!17Tr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!17Tr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 424w, https://substackcdn.com/image/fetch/$s_!17Tr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 848w, https://substackcdn.com/image/fetch/$s_!17Tr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 1272w, https://substackcdn.com/image/fetch/$s_!17Tr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!17Tr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png" width="1456" height="436" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:436,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59365,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/207697291?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!17Tr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 424w, https://substackcdn.com/image/fetch/$s_!17Tr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 848w, https://substackcdn.com/image/fetch/$s_!17Tr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 1272w, https://substackcdn.com/image/fetch/$s_!17Tr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a639f24-1c3f-4da9-ab4b-7d5645319fcd_1564x468.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128421;&#65039; Create the Event</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;947c4f92-ce3c-434f-85bc-3a1d2618b1fd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">php artisan make:event RideCompleted</code></pre></div><p>Laravel creates:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;17a667e2-c769-46bc-8e77-0b4f1080dae1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">app/Events/RideCompleted.php</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;00889bd8-4f0c-48a9-93be-2f5bc0eef0bf&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">&lt;?php

namespace App\Events;

use App\Models\Ride;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class RideCompleted
{
    use Dispatchable, SerializesModels;

    public function __construct(
        public Ride $ride
    ) {}
}</code></pre></div><p>The Event contains only the data.</p><p>It does <strong>not</strong></p><ul><li><p>Send emails</p></li><li><p>Charge payment</p></li><li><p>Generate invoices</p></li></ul><p>It simply announces</p><blockquote><p>A ride has been completed.</p></blockquote><h2>&#10004;&#65039; Controller</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;c1f85dd1-e431-4020-a1d9-17be5d97db6c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">&lt;?php

namespace App\Http\Controllers;

use App\Events\RideCompleted;
use App\Models\Ride;
use Illuminate\Http\Request;

class RideController extends Controller
{
    public function completeRide(Request $request)
    {
        $ride = Ride::create([
            'driver_id' =&gt; $request-&gt;driver_id,
            'rider_id'  =&gt; $request-&gt;rider_id,
            'fare'      =&gt; $request-&gt;fare,
            'status'    =&gt; 'completed',
        ]);

        RideCompleted::dispatch($ride);

        return response()-&gt;json([
            'message' =&gt; 'Ride completed successfully.'
        ]);
    }
}</code></pre></div><p>Notice how the controller has only <strong>two responsibilities</strong>:</p><ul><li><p>Save the ride</p></li><li><p>Dispatch the event</p></li></ul><h2>&#10004;&#65039; Charge Customer Listener</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;5a2c78a5-267e-4ca8-aafd-237a013fe5fd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">php artisan make:listener ChargeCustomer</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;4bffd337-b925-4b3a-9e3f-dc99d38de9ce&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">&lt;?php

namespace App\Listeners;

use App\Events\RideCompleted;
use App\Services\PaymentService;

class ChargeCustomer
{
    public function __construct(
        protected PaymentService $paymentService
    ) {}

    public function handle(RideCompleted $event)
    {
        $this-&gt;paymentService-&gt;charge(
            $event-&gt;ride
        );
    }
}</code></pre></div><h3>Responsibility</h3><p>Only charge payment.</p><p>Nothing else.</p><h2>&#10004;&#65039; Send Receipt Email</h2><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;04b4773c-932d-4d8b-87a1-78a91ffc6845&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">php artisan make:listener SendRideReceipt</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;f9ad6d31-cc43-4864-ad1f-7a43bec46e40&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">&lt;?php

namespace App\Listeners;

use App\Events\RideCompleted;
use App\Mail\RideReceiptMail;
use Illuminate\Support\Facades\Mail;

class SendRideReceipt
{
    public function handle(RideCompleted $event)
    {
        Mail::to($event-&gt;ride-&gt;rider-&gt;email)
            -&gt;send(
                new RideReceiptMail($event-&gt;ride)
            );
    }
}</code></pre></div><p>we need to create a <code>GenerateInvoice</code>, <code>UpdateDriverWallet</code>, <code>RewardRider</code>, and <code>StoreRideAnalytics all this listeners</code>.</p><h2>&#10004;&#65039; Register Listeners</h2><p>In your event registration (such as an event service provider), associate the event with its listeners.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;7c079c67-3691-4116-842b-410737b950c9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">protected $listen = [

    RideCompleted::class =&gt; [

        ChargeCustomer::class,

        SendRideReceipt::class,

        GenerateInvoice::class,

        UpdateDriverWallet::class,

        RewardRider::class,

        StoreRideAnalytics::class,

    ],

];</code></pre></div><div><hr></div><h2>&#127942; Conclusion</h2><p>Laravel Events and Listeners provide a clean and elegant way to build scalable, maintainable applications by separating business logic into independent, reusable components. Instead of overloading controllers with multiple responsibilities, you can simply dispatch an event and let dedicated listeners handle the tasks that follow. This approach promotes loose coupling, improves code organization, and makes it easier to extend your application as new business requirements arise.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🔰How Laravel chunk() Silently Lost Thousands of Records]]></title><description><![CDATA[Laravel chunk() vs chunkById(): The Production Bug Nobody Talks About]]></description><link>https://newsletter.priteshbhanushali.com/p/how-laravel-chunk-silently-lost-thousands</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/how-laravel-chunk-silently-lost-thousands</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 18 Jul 2026 04:30:28 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e8dc0fcf-b1ce-4564-8762-d14248b88562_1448x1086.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vq-U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vq-U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 424w, https://substackcdn.com/image/fetch/$s_!vq-U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 848w, https://substackcdn.com/image/fetch/$s_!vq-U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!vq-U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vq-U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png" width="1448" height="1086" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1086,&quot;width&quot;:1448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1567901,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/207187154?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vq-U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 424w, https://substackcdn.com/image/fetch/$s_!vq-U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 848w, https://substackcdn.com/image/fetch/$s_!vq-U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 1272w, https://substackcdn.com/image/fetch/$s_!vq-U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8672f634-bcd3-43a9-9182-7bf343c7e74f_1448x1086.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Batch processing is one of the most common tasks in Laravel applications. Whether you&#8217;re sending emails, generating reports, syncing data, or processing millions of records, chances are you&#8217;ve used the <code>chunk()</code> method.</p><p>It looks simple, performs well, and is recommended throughout the Laravel documentation.</p><p>But there&#8217;s a hidden problem.</p><p>If you&#8217;re updating records while processing them, <code>chunk()</code><strong> can silently skip rows</strong>, causing incomplete processing without throwing a single exception.</p><p>I first encountered this issue while working on a large-scale document import project for one of our enterprise clients. The system was responsible for importing and processing <strong>millions of documents</strong>, so we built the solution using <strong>Laravel Queues</strong>, <strong>Artisan commands</strong>, and <code>chunk()</code> to process the data efficiently in batches.</p><p>The implementation appeared to work perfectly during development and testing. However, in production, we discovered that some documents were never processed. There were no exceptions, no failed jobs, and no obvious signs of failure yet a portion of the data remained unprocessed.</p><p>After a detailed investigation, we traced the root cause to our use of Laravel&#8217;s <code>chunk()</code> method while updating the same records being processed. This experience reinforced an important lesson: choosing the right batching strategy is just as critical as writing the business logic itself, especially when working with large-scale data processing systems.</p><p>Let&#8217;s understand why this happens and how to avoid it.</p><div><hr></div><h2>&#128194; The Document Processing Workflow</h2><p>Imagine you have a table that stores imported documents.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ys_4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ys_4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 424w, https://substackcdn.com/image/fetch/$s_!Ys_4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 848w, https://substackcdn.com/image/fetch/$s_!Ys_4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!Ys_4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ys_4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png" width="1418" height="1000" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1000,&quot;width&quot;:1418,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:107815,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/207187154?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Ys_4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 424w, https://substackcdn.com/image/fetch/$s_!Ys_4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 848w, https://substackcdn.com/image/fetch/$s_!Ys_4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!Ys_4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e299cbf-aa67-4887-873c-8a32e041cebf_1418x1000.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Each document goes through the following steps:</p><ol><li><p>Upload it to cloud storage.</p></li><li><p>Update its status to <code>processed</code>.</p></li></ol><p>To avoid loading millions of records into memory, we used Laravel's <code>chunk()</code> method.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;7403c253-035e-48c1-b8a2-d0663585531e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">Document::where('status', 'pending')
    -&gt;chunk(1000, function ($documents) {
        foreach ($documents as $document) {
            
            // Upload to storage
            
            $document-&gt;update([
                'status' =&gt; 'processed'
            ]);
        }
    });</code></pre></div><p>At first glance, this looks like the perfect solution.</p><p>But there&#8217;s a hidden problem.</p><div><hr></div><h2>&#128452;&#65039; What Happens Behind the Scenes?</h2><p>Laravel internally executes queries similar to:</p><pre><code><code>SELECT *
FROM documents
WHERE status = 'pending'
LIMIT 1000 OFFSET 0;</code></code></pre><p>The first chunk returns:</p><pre><code><code>1
2
3
...
1000</code></code></pre><p>Each document is processed successfully.</p><p>Its status changes from:</p><pre><code><code>Pending</code></code></pre><p>to</p><pre><code><code>Processed</code></code></pre><p>Everything still looks correct.</p><div><hr></div><h2>&#128260; The Dataset Changes</h2><p>After processing the first batch, those records no longer satisfy:</p><pre><code><code>WHERE status = 'pending'</code></code></pre><p>The pending dataset now starts from the next unprocessed documents.</p><p>For simplicity, imagine our table contains only ten documents.</p><p>The remaining pending documents are now:</p><pre><code><code>4
5
6
7
8
9
10</code></code></pre><p>Notice that documents <strong>1, 2, and 3</strong> have disappeared from the result because their status changed.</p><div><hr></div><h2>&#9888;&#65039; Here&#8217;s Where the Problem Begins</h2><p>Laravel now executes the next query using OFFSET.</p><pre><code><code>LIMIT 3 OFFSET 3</code></code></pre><p>The current pending dataset is:</p><pre><code><code>Index    Document ID

0  ---&gt; 4
1  ---&gt; 5
2  ---&gt; 6
3  ---&gt; 7
4  ---&gt; 8
5  ---&gt; 9
6  ---&gt; 10</code></code></pre><p>OFFSET skips the first three rows.</p><pre><code><code>&#10060; 4
&#10060; 5
&#10060; 6</code></code></pre><p>Laravel processes only:</p><pre><code><code>&#9989; 7
&#9989; 8
&#9989; 9</code></code></pre><p>Documents <strong>4, 5, and 6</strong> are never processed.</p><p>Document <strong>10</strong> also remains pending because no further chunk includes it in this small example.</p><p>The Artisan command completes successfully.</p><p>The queue reports success.</p><p>No exception is thrown.</p><p>But several documents remain unprocessed.</p><div><hr></div><h2>&#128202; Visualizing the Problem</h2><pre><code><code>Initial Pending Documents

1 2 3 4 5 6 7 8 9 10

        &#9474;
        &#9660;

Process First Chunk

[1 2 3]

        &#9474;
        &#9660;

Update Status = Processed

Remaining Pending

4 5 6 7 8 9 10

        &#9474;
        &#9660;

Laravel Executes

OFFSET 3

        &#9474;
        &#9660;

&#10060; Skip

4 5 6

        &#9474;
        &#9660;

Process Next Chunk

7 8 9</code></code></pre><p>This is exactly why some documents never get processed.</p><div><hr></div><h2>&#129300; Why Does This Happen?</h2><p>The issue isn&#8217;t with Laravel.</p><p>It&#8217;s with <strong>OFFSET-based pagination</strong>.</p><p>When your code updates the same records that your query depends on, the result set changes after every batch.</p><p>However, OFFSET continues counting based on the old position.</p><p>As a result, some rows are skipped entirely.</p><h2>&#9989; The Correct Solution: <code>chunkById()</code></h2><h3>Instead of relying on OFFSET, Laravel provides <code>chunkById()</code>.</h3><pre><code><code>Document::where('status', 'pending')
    -&gt;chunkById(1000, function ($documents) {

        foreach ($documents as $document) {

            // Process document

            $document-&gt;update([
                'status' =&gt; 'processed'
            ]);

        }

    });</code></code></pre><p>Rather than using OFFSET, Laravel keeps track of the last processed primary key.</p><p>The queries become:</p><pre><code><code>WHERE id &gt; 0
LIMIT 1000

&#8595;

WHERE id &gt; 1000
LIMIT 1000

&#8595;

WHERE id &gt; 2000
LIMIT 1000</code></code></pre><p>Since primary keys don&#8217;t change when the status is updated, every document is processed exactly once.</p><p>No skipped records.</p><p>No incomplete imports.</p><div><hr></div><h2>&#9889; An Additional Performance Benefit</h2><h3><code>chunkById()</code> isn&#8217;t just safer it also performs better on large datasets.</h3><p>With <code>chunk()</code>, MySQL must scan and discard rows to satisfy large OFFSET values.</p><p>For example:</p><pre><code><code>LIMIT 1000 OFFSET 500000</code></code></pre><p>The database still has to read the first <strong>500,000 rows</strong> before returning the next 1,000.</p><p>With <code>chunkById()</code>:</p><pre><code><code>WHERE id &gt; 500000
LIMIT 1000</code></code></pre><p>MySQL can efficiently use the primary key index, making queries much faster for millions of records.</p><div><hr></div><h2>&#128221; Best Practices</h2><p>When building document processing pipelines:</p><ul><li><p>&#9989; Use <code>chunk()</code> only for read-only operations.</p></li><li><p>&#9989; Use <code>chunkById()</code> if you&#8217;re updating or deleting records.</p></li><li><p>&#9989; Keep processing jobs idempotent so they can be retried safely.</p></li><li><p>&#9989; Test batch-processing logic with production-sized datasets.</p></li><li><p>&#9989; Monitor processed vs. pending record counts after every batch.</p></li></ul><div><hr></div><h2>&#127919; Key Takeaways</h2><ul><li><p>&#128683; <code>chunk()</code> uses OFFSET pagination.</p></li><li><p>&#128683; Updating the records being processed changes the result set.</p></li><li><p>&#128683; OFFSET can silently skip records.</p></li><li><p>&#9989; <code>chunkById()</code> navigates using the primary key instead of OFFSET.</p></li><li><p>&#9989; Every document is processed exactly once.</p></li><li><p>&#9989; It also scales better for millions of records.</p></li></ul><div><hr></div><h2>&#127881; Final Thoughts</h2><p>This wasn&#8217;t a Laravel bug it was a subtle behavior of OFFSET-based pagination that surfaced in a real production workload.</p><p>Because the job completed successfully, the issue was difficult to detect. We only discovered it after reconciling the number of imported documents against the number of processed documents.</p><p>That experience taught our team an important lesson:</p><blockquote><p><strong>Whenever your batch job modifies the same records it is reading, </strong><code>chunkById()</code><strong> should be your default choice.</strong></p></blockquote><p>It&#8217;s a small change in code, but it can prevent missing records, incomplete imports, and long hours of production debugging especially when processing millions of documents.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🎯Laravel Database Seeders: Beyond Dummy Data]]></title><description><![CDATA[How to manage roles, permissions, and master data safely across local, staging, and production environments]]></description><link>https://newsletter.priteshbhanushali.com/p/laravel-database-seeders-beyond-dummy</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/laravel-database-seeders-beyond-dummy</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 03 Jan 2026 05:00:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!I-HD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Setting up consistent data across environments is one of the most common challenges in Laravel projects. Whether you are onboarding a new developer, preparing demo data for QA, or running automated tests, manually inserting records into the database is error-prone and time-consuming.</p><p>Laravel solves this problem elegantly using <strong>Database Seeders</strong>.</p><div><hr></div><h2>&#128304;What Is Database Seeding in Laravel?</h2><p><strong>Database seeding</strong> is the process of populating your database with predefined or fake data using PHP classes.</p><p>Laravel seeders allow you to:</p><ul><li><p>Insert default records (roles, permissions, admin users)</p></li><li><p>Generate dummy data for testing</p></li><li><p>Maintain consistent data across environments</p></li><li><p>Avoid repetitive manual database setup</p></li></ul><p>Seeders are especially useful in <strong>local</strong>, <strong>testing</strong>, and <strong>staging</strong> environments.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!I-HD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!I-HD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!I-HD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2524044,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/183123429?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!I-HD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!I-HD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F975979e6-b54a-4850-a6d9-b97062b6b633_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#9881;&#65039;How to Create a Database Seeder in Laravel</h2><p>Creating a database seeder in Laravel is straightforward and follows a clear, standardized workflow. Seeders allow you to define database data using PHP code, making your setup repeatable and version-controlled.</p><h4>&#128736;&#65039; Laravel provides an Artisan command to generate a seeder.</h4><pre><code>php artisan make:seeder TransactionStatusSeeder</code></pre><p>This command creates a new seeder file inside the <code>database/seeders</code> directory.</p><pre><code>database/seeders/TransactionStatusSeeder.php</code></pre><h4>&#129513; Understand the Seeder Structure</h4><p>A basic seeder class looks like this:</p><pre><code>use Illuminate\Database\Seeder;

class TransactionStatusSeeder extends Seeder
{
    public function run()
    {
        // Seeding logic goes here
    }
}</code></pre><ul><li><p>Every seeder extends the <code>Seeder</code> class</p></li><li><p>The <code>run()</code> method is executed when the seeder runs</p></li><li><p>All database insert logic lives inside this method</p></li></ul><h4>&#129514;Add Data to the Seeder</h4><p>Assume you have a table called <code>transaction_statuses</code> with columns:</p><ul><li><p><code>code</code></p></li><li><p><code>label</code></p></li><li><p><code>is_active</code></p></li></ul><p>Now define the seeding logic:</p><pre><code>use Illuminate\Database\Seeder;
use App\Models\TransactionStatus;

class TransactionStatusSeeder extends Seeder
{
    public function run()
    {
        $statuses = [
            ['code' =&gt; 'PENDING',   'label' =&gt; 'Pending',   'is_active' =&gt; true],
            ['code' =&gt; 'POSTED',    'label' =&gt; 'Posted',    'is_active' =&gt; true],
            ['code' =&gt; 'REVERSED',  'label' =&gt; 'Reversed',  'is_active' =&gt; true],
            ['code' =&gt; 'VOIDED',    'label' =&gt; 'Voided',    'is_active' =&gt; false],
        ];

        foreach ($statuses as $status) {
            TransactionStatus::updateOrCreate(
                ['code' =&gt; $status['code']],
                $status
            );
        }
    }
}</code></pre><h5>Why <code>updateOrCreate()</code>?</h5><ul><li><p>Prevents duplicate records</p></li><li><p>Allows controlled updates to labels or flags</p></li><li><p>Safe to run in production</p></li><li><p>Ideal for <strong>master data</strong></p></li></ul><p>This pattern is widely used in <strong>finance, insurance, and accounting systems</strong>.</p><div><hr></div><h4>&#9997;&#65039; Register the Seeder</h4><pre><code>class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            TransactionStatusSeeder::class,
        ]);
    }
}</code></pre><p>The <code>DatabaseSeeder</code> class is the <strong>central entry point</strong> for all database seeders in a Laravel application. Whenever you run a seeding command, Laravel starts execution from this class and then calls all registered seeders in the defined order.</p><p>Understanding <code>DatabaseSeeder</code> is essential because it controls <strong>what data gets seeded, when it gets seeded, and in which environment</strong>.</p><h4>&#128193; Where Is DatabaseSeeder Located?</h4><p>Laravel automatically creates this file at:</p><pre><code><code>database/seeders/DatabaseSeeder.php</code></code></pre><pre><code>database/seeders/
 &#9500;&#9472;&#9472; DatabaseSeeder.php
 &#9500;&#9472;&#9472; TransactionStatusSeeder.php
 &#9492;&#9472;&#9472; RoleSeeder.php</code></pre><p>This file exists by default in every Laravel project and acts as the <strong>orchestrator</strong> for all other seeders.</p><h4>&#128196; Basic Structure of DatabaseSeeder</h4><pre><code>use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        // Call individual seeders here
    }
}</code></pre><h5>Key Points</h5><ul><li><p><code>DatabaseSeeder</code> extends the base <code>Seeder</code> class</p></li><li><p>The <code>run()</code> method is the <strong>starting point</strong></p></li><li><p>All child seeders are invoked from here</p></li></ul><h4>&#9654;&#65039; How DatabaseSeeder Works</h4><p>When you run:</p><pre><code>php artisan db:seed</code></pre><p>Laravel performs the following steps:</p><ol><li><p>Bootstraps the application</p></li><li><p>Loads <code>DatabaseSeeder</code></p></li><li><p>Executes the <code>run()</code> method</p></li><li><p>Calls all registered seeders in sequence</p></li><li><p>Executes each seeder&#8217;s <code>run()</code> method</p></li></ol><p>This makes <code>DatabaseSeeder</code> the <strong>single source of truth</strong> for seeding.</p><h4>&#128313;Registering Seeders in DatabaseSeeder</h4><p>To execute a seeder, it must be registered using the <code>call()</code> method.</p><pre><code>class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            RoleSeeder::class,
            PermissionSeeder::class,
            UserSeeder::class,
        ]);
    }
}</code></pre><h5>Why Order Matters</h5><ul><li><p>Roles must exist before permissions are assigned</p></li><li><p>Permissions must exist before user-role mapping</p></li><li><p>Prevents foreign key constraint errors</p></li></ul><h4>&#128313;Calling Seeders Conditionally</h4><p>You can control which seeders run based on the environment.</p><pre><code>public function run()
{
    $this-&gt;call([
        RoleSeeder::class,
        PermissionSeeder::class,
    ]);

    if (app()-&gt;environment(['local', 'staging'])) {
        $this-&gt;call(UserSeeder::class);
    }
}</code></pre><p>This ensures:</p><ul><li><p>Production stays clean</p></li><li><p>Test users are not created in live systems</p></li></ul><h2>&#128450;&#65039; Grouping Seeders for Better Organization</h2><p>In large applications, it&#8217;s a good practice to group related seeders.</p><pre><code>public function run()
{
    $this-&gt;call([
        MasterDataSeeder::class,
        AuthorizationSeeder::class,
        ConfigurationSeeder::class,
    ]);
}</code></pre><p>Each group seeder can internally call smaller seeders.</p><pre><code>class AuthorizationSeeder extends Seeder
{
    public function run()
    {
        $this-&gt;call([
            RoleSeeder::class,
            PermissionSeeder::class,
            RolePermissionSeeder::class,
        ]);
    }
}</code></pre><p>This improves:</p><ul><li><p>Readability</p></li><li><p>Maintainability</p></li><li><p>Scalability</p></li></ul><div><hr></div><h2>&#9654;&#65039; Running the Seeder Explicitly via Artisan</h2><pre><code>php artisan db:seed --class=TransactionStatusSeeder</code></pre><h4>Running DatabaseSeeder Explicitly</h4><p>Although <code>DatabaseSeeder</code> runs by default, you can explicitly call it:</p><pre><code>php artisan db:seed --class=DatabaseSeeder</code></pre><p>This is useful in:</p><ul><li><p>CI/CD pipelines</p></li><li><p>Controlled production deployments</p></li><li><p>Multi-tenant seeding workflows</p></li></ul><div><hr></div><h2>&#128452;&#65039;Database Seeders: A Game-Changer for Multi-Tenant Applications</h2><p>In a <strong>multi-tenant architecture</strong>, a single application serves <strong>multiple tenants</strong>, but each tenant&#8217;s data must remain <strong>isolated</strong>.<br>This makes database seeding fundamentally different from a single-database application.</p><p>Laravel seeders play a <strong>critical role</strong> in initializing and maintaining <strong>consistent data across all tenants</strong>.</p><h4>&#128313; The Core Challenge in Multi-Tenant Seeding</h4><p>In a normal Laravel app:</p><ul><li><p>One database</p></li><li><p>One <code>DatabaseSeeder</code></p></li><li><p>Seed once &#8594; done</p></li></ul><p>In a <strong>multi-tenant system</strong>:</p><ul><li><p>Multiple tenant databases or schemas</p></li><li><p>Each tenant requires <strong>the same base data</strong></p></li><li><p>Seeding must happen <strong>per tenant</strong>, not globally</p></li></ul><p>&#128204; Examples of tenant-specific data:</p><ul><li><p>Roles &amp; permissions</p></li><li><p>Default users</p></li><li><p>Configuration settings</p></li><li><p>Accounting rules</p></li><li><p>Commission slabs</p></li><li><p>Feature flags</p></li></ul><div><hr></div><p>The Tenancy for Laravel package, along with the Spatie package, helps manage tenant isolation and provides tenant-aware features for Laravel applications.</p><p><strong>Tenant-aware Artisan Commands</strong></p><ul><li><p>You can run migrations or seeders <strong>per tenant</strong>.</p></li><li><p>Example:</p></li></ul><pre><code>php artisan tenants:migrate
php artisan tenants:seed --class=SomeSeeder</code></pre><p>Without this, running <code>php artisan db:seed</code> would only affect the central database.</p><ul><li><p>The package automatically switches the database connection and context based on the current tenant.</p></li><li><p>This makes queries &#8220;tenant-aware&#8221; without changing your code everywhere.</p></li></ul><p><strong>&#128221; Seed all tenants with default DatabaseSeeder</strong>:</p><pre><code>php artisan tenants:seed</code></pre><p><strong>&#128221; Seed a specific tenant seeder</strong>:</p><pre><code>php artisan tenants:seed --class=TransactionStatusSeeder</code></pre><p><strong>&#128221; Seed a specific tenant (if supported by package)</strong>:</p><pre><code>php artisan tenants:seed --tenant=1 --class=TransactionStatusSeeder</code></pre><div><hr></div><h2>&#128221;Best Practices for Laravel Seeders</h2><p>&#9989; Keep seeders <strong>small and focused</strong><br>&#9989; Use <strong>factories</strong> for large datasets<br>&#9989; Separate <strong>master data</strong> (roles, permissions)<br>&#9989; Use <strong>environment checks</strong><br>&#10060; Never seed sensitive production data<br>&#10060; Avoid running destructive commands in production</p><div><hr></div><h2>&#9888;&#65039;Common Mistakes and Troubleshooting</h2><h4>Seeder not running?</h4><ul><li><p>Ensure it is registered in <code>DatabaseSeeder</code></p></li></ul><h4>Foreign key constraint errors?</h4><ul><li><p>Disable foreign key checks temporarily</p></li><li><p>Ensure correct seeding order</p></li></ul><h4>Duplicate records?</h4><ul><li><p>Add existence checks before inserting</p></li></ul><div><hr></div><h2>&#127760;Real-World Use Cases</h2><ul><li><p>Creating default admin users</p></li><li><p>Seeding roles and permissions</p></li><li><p>Generating transactions for QA testing</p></li><li><p>Preparing demo data for stakeholders</p></li><li><p>Speeding up onboarding for new developers</p></li></ul><div><hr></div><h2>&#129525; Wrapping Up</h2><p>Laravel Database Seeders are a powerful yet simple tool that can save hours of manual setup and ensure consistency across environments.</p><p>When used correctly&#8212;with factories, best practices, and environment awareness&#8212;seeders become an essential part of any professional Laravel project.</p><blockquote><p><strong>A well-designed seeding strategy is not just about dummy data&#8212;it&#8217;s about predictable, repeatable, and reliable development environments.</strong></p></blockquote><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🔐 Encryption vs Hashing vs Encoding in Laravel]]></title><description><![CDATA[Same Data. Three Techniques. One Costly Mistake Away from Disaster.]]></description><link>https://newsletter.priteshbhanushali.com/p/encryption-vs-hashing-vs-encoding</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/encryption-vs-hashing-vs-encoding</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Wed, 17 Dec 2025 04:31:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NhMa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>&#128680; Introduction: One Wrong Choice Can Break Your Security</h3><p>Every Laravel developer works with sensitive data.</p><p>Passwords.<br>API keys.<br>Bank accounts.<br>Personal identifiers.</p><p>Yet one mistake is shockingly common:</p><blockquote><p>Treating <strong>encryption</strong>, <strong>hashing</strong>, and <strong>encoding</strong> as the same thing.</p></blockquote><p>They&#8217;re not.</p><p>Using the wrong technique doesn&#8217;t just cause bugs &#8212; it can lead to <strong>security breaches, failed audits, compliance violations, and irreversible data loss</strong>.</p><p>In this guide, we&#8217;ll clearly break down:</p><ul><li><p>&#128269; What <strong>Encryption</strong>, <strong>Hashing</strong>, and <strong>Encoding</strong> really are</p></li><li><p>&#129504; When you should use each one (and when you absolutely shouldn&#8217;t)</p></li><li><p>&#128736;&#65039; <strong>Real Laravel examples</strong> you can copy-paste today</p></li><li><p>&#128683; Costly mistakes even experienced developers make</p></li></ul><p>This isn&#8217;t theory &#8212; it&#8217;s written for <strong>real Laravel applications</strong>: APIs, SaaS platforms, fintech products, and multi-tenant systems.</p><div><hr></div><h3>1&#65039;&#8419; Encryption: Lock It, Unlock It (When You Must)</h3><h4>&#128272; What Is Encryption?</h4><p>Encryption transforms <strong>readable data (plaintext)</strong> into <strong>unreadable data (ciphertext)</strong> using a <strong>secret key</strong>.</p><p>&#128073; <strong>It is reversible</strong> &#8212; if (and only if) you have the correct key.</p><p>Think of encryption as a <strong>secure locker</strong>:</p><ul><li><p>You lock the data</p></li><li><p>You unlock it only when truly necessary</p></li></ul><h4>&#9989; When Should You Use Encryption?</h4><p>Use encryption <strong>only</strong> when the original value must be retrieved later.</p><p>&#10004; Bank account numbers<br>&#10004; API tokens &amp; secrets<br>&#10004; OAuth credentials<br>&#10004; Personally Identifiable Information (PII)</p><h4>&#128736;&#65039; Laravel Encryption (Built-In &amp; Battle-Tested)</h4><p>Laravel ships with strong encryption using <strong>AES-256-CBC</strong> &#8212; no setup required.</p><h4>Encrypting Data</h4><pre><code>use Illuminate\Support\Facades\Crypt;

$encrypted = Crypt::encryptString(&#8217;1234-5678-9012-3456&#8217;);</code></pre><h4>Decrypting Data</h4><pre><code>$decrypted = Crypt::decryptString($encrypted);
</code></pre><p>&#10004; Key-based<br>&#10004; Secure<br>&#10004; Fully reversible</p><h4>&#127757; Real-World Example: Storing Bank Details Securely</h4><pre><code>Schema::create(&#8217;client_bank_accounts&#8217;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;string(&#8217;account_holder_name&#8217;);
    $table-&gt;text(&#8217;account_number&#8217;); // encrypted
    $table-&gt;text(&#8217;ifsc_code&#8217;);       // encrypted
    $table-&gt;timestamps();
});</code></pre><pre><code>$bank-&gt;account_number = Crypt::encryptString($request-&gt;account_number);
$bank-&gt;ifsc_code = Crypt::encryptString($request-&gt;ifsc_code);
$bank-&gt;save();</code></pre><p>&#128274; Even if your database is leaked, the data remains useless without <code>APP_KEY</code>.</p><div><hr></div><h3>2&#65039;&#8419; Hashing: Verify Without Ever Knowing</h3><h4>&#128273; What Is Hashing?</h4><p>Hashing converts data into a <strong>fixed-length string</strong> using a mathematical algorithm.</p><p>&#128073; <strong>It is irreversible</strong> &#8212; forever.</p><p>You don&#8217;t unlock hashed data.<br>You <strong>compare</strong> against it.</p><h4>&#9989; When Should You Use Hashing?</h4><p>Use hashing when you <strong>never need the original value again</strong>.</p><p>&#10004; Passwords<br>&#10004; PINs<br>&#10004; Security answers</p><h4>&#128736;&#65039; Laravel Hashing Example</h4><p>Laravel uses <strong>bcrypt</strong> or <strong>Argon2</strong>, both industry-approved.</p><h4>Hashing a Password</h4><pre><code>use Illuminate\Support\Facades\Hash;

$hashed = Hash::make(&#8217;my-secret-password&#8217;);
</code></pre><h4>Verifying a Password</h4><pre><code>Hash::check(&#8217;my-secret-password&#8217;, $hashed); // true</code></pre><p>&#10004; One-way<br>&#10004; Automatically salted<br>&#10004; Resistant to brute-force attacks</p><h4>&#128683; The Biggest Security Mistake: Encrypting Passwords</h4><p>&#10060; <strong>Never do this</strong>:</p><pre><code><code>Crypt::encryptString(&#8217;password123&#8217;);
</code></code></pre><p>Why this is dangerous:</p><ul><li><p>Anyone with <code>APP_KEY</code> can decrypt every password</p></li><li><p>Violates OWASP standards</p></li><li><p>Fails security audits instantly</p></li></ul><p>&#9989; <strong>Passwords must always be hashed &#8212; never encrypted.</strong></p><div><hr></div><h3>3&#65039;&#8419; Encoding: Data Travel, Not Data Protection</h3><h4>&#128260; What Is Encoding?</h4><p>Encoding transforms data into another format so it can be <strong>safely transmitted or stored</strong>.</p><p>&#128073; <strong>It provides ZERO security.</strong></p><p>Encoding is about <strong>compatibility</strong>, not protection.</p><h4>&#9989; When Should You Use Encoding?</h4><p>&#10004; Sending data over APIs<br>&#10004; URLs<br>&#10004; JSON responses<br>&#10004; File uploads</p><h4>&#128736;&#65039; Common Encoding Examples</h4><h4>Base64 Encoding</h4><pre><code><code>$encoded = base64_encode(&#8217;Hello World&#8217;);
$decoded = base64_decode($encoded);
</code></code></pre><h4>JSON Encoding</h4><pre><code><code>$data = [&#8217;name&#8217; =&gt; &#8216;John&#8217;, &#8216;role&#8217; =&gt; &#8216;admin&#8217;];

$json  = json_encode($data);
$array = json_decode($json, true);
</code></code></pre><p>&#9888;&#65039; Anyone can decode encoded data in seconds.</p><div><hr></div><h3>&#128269; Encryption vs Hashing vs Encoding (At a Glance)</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NhMa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NhMa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 424w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 848w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!NhMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png" width="1200" height="1200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1200,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:550378,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/181507383?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NhMa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 424w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 848w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1272w, https://substackcdn.com/image/fetch/$s_!NhMa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe721bacc-1960-4c3b-b35d-31e80bd143dd_1200x1200.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128683; Costly Mistakes Developers Still Make</h3><p>&#10060; Treating encoding as security<br>&#10060; Encrypting passwords<br>&#10060; Logging decrypted sensitive data<br>&#10060; Sharing <code>APP_KEY</code> across environments<br>&#10060; Skipping key rotation policies</p><div><hr></div><h3>&#9989; Laravel Security Best Practices</h3><p>&#10004; Hash all passwords with <code>Hash::make()</code><br>&#10004; Encrypt sensitive data with <code>Crypt</code><br>&#10004; Protect your <code>APP_KEY</code> like production credentials<br>&#10004; Restrict access to decrypted data<br>&#10004; Rotate keys if compromised</p><div><hr></div><h3>&#129504; Final Thoughts: Choose Wisely or Pay Later</h3><p>These three concepts are <strong>not interchangeable</strong> &#8212; and confusing them is expensive.</p><ul><li><p>&#128272; <strong>Encryption</strong> &#8594; for data you must retrieve</p></li><li><p>&#128273; <strong>Hashing</strong> &#8594; for secrets you must never see again</p></li><li><p>&#128260; <strong>Encoding</strong> &#8594; for data movement, not protection</p></li></ul><p>If you&#8217;re building <strong>fintech, healthcare, SaaS, or multi-tenant systems</strong>, this knowledge isn&#8217;t optional &#8212; it&#8217;s foundational.</p><p><strong>Secure code is not about more tools.<br>It&#8217;s about using the right one.</strong></p><p>Happy coding &#8212; and stay secure &#128272;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🔒Mastering Encryption in Laravel: A Complete Guide with Real-World Examples]]></title><description><![CDATA[A Real-World Guide to Securely Storing Sensitive Data (Like Client Bank Info) in Laravel]]></description><link>https://newsletter.priteshbhanushali.com/p/mastering-encryption-in-laravel-a</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/mastering-encryption-in-laravel-a</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sat, 13 Dec 2025 09:30:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!QIYY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today&#8217;s digital world, data is one of the most valuable assets &#8212; and also one of the most vulnerable. Whether you&#8217;re building a SaaS platform, a healthcare portal, or a financial app, protecting sensitive information is <strong>not optional</strong>. Laravel makes this easier with its built-in, developer-friendly <strong>encryption system</strong>.</p><p>This guide will help you understand:</p><ul><li><p><strong>What</strong> encryption is</p></li><li><p><strong>How</strong> Laravel handles encryption</p></li><li><p><strong>When</strong> you should use it</p></li><li><p><strong>Best practices</strong> every developer must follow</p></li><li><p><strong>Real-world use cases</strong> to apply in your projects</p></li></ul><p>Let&#8217;s dive in! &#129504;</p><div><hr></div><h2>&#129513; <strong>1. What Is Encryption?</strong></h2><p><strong>Encryption is the process of converting readable data (plaintext) into unreadable data (ciphertext)</strong> using a key.<br>Only someone with the correct key can decrypt the data.</p><h3>Why We Need Encryption?</h3><ul><li><p>To protect sensitive data</p></li><li><p>To comply with security standards</p></li><li><p>To secure database-stored information</p></li><li><p>To prevent identity theft, fraud, or data leaks</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QIYY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QIYY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!QIYY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg" width="1200" height="628" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:628,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:87583,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/181415377?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438b597b-e083-43cc-8da3-a53e06735453_1200x1200.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QIYY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QIYY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d93c22-34ed-491c-8402-34745d3a959e_1200x628.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3>Laravel&#8217;s Encryption</h3><p>Laravel uses <strong>OpenSSL</strong> and the industry-standard <strong>AES-256-CBC</strong> or <strong>AES-128-CBC</strong> encryption.</p><p>It is:</p><ul><li><p>Secure</p></li><li><p>Fast</p></li><li><p>Easy to use</p></li><li><p>Opinionated (removes complexity)</p></li></ul><div><hr></div><h2>&#9881;&#65039; <strong>2. How Laravel Encryption Works</strong></h2><p>Laravel uses a secret key stored in <code>.env</code> &#8594; <code>APP_KEY</code>.</p><pre><code>APP_KEY=base64:JQ1XC77tVRa1Tt56Zl................</code></pre><p>This key is used to encrypt/decrypt values.</p><h3>&#10004;&#65039; <strong>Encrypting Data</strong></h3><pre><code>use Illuminate\Support\Facades\Crypt;

$encrypted = Crypt::encryptString(&#8217;Hello Pritesh&#8217;);</code></pre><h3>&#10004;&#65039; <strong>Decrypting Data</strong></h3><pre><code>use Illuminate\Support\Facades\Crypt;

$decrypted = Crypt::decryptString($encrypted);</code></pre><div><hr></div><h2>&#128338; <strong>3. When Should You Use Encryption?</strong></h2><p>You should NOT encrypt everything.</p><p>Use encryption when storing or transmitting <strong>highly sensitive information</strong> such as:</p><h3>&#10004;&#65039; Personally identifiable information (PII)</h3><ul><li><p>Aadhaar / SSN</p></li><li><p>Phone numbers</p></li><li><p>Government IDs</p></li><li><p>Passport numbers</p></li></ul><h3>&#10004;&#65039; Financial data</h3><ul><li><p>Bank account details</p></li><li><p>Credit card numbers</p></li><li><p>Transaction tokens</p></li></ul><h3>&#10004;&#65039; Authentication-related data</h3><ul><li><p>API keys</p></li><li><p>Access tokens</p></li><li><p>Refresh tokens</p></li><li><p>OAuth secrets</p></li></ul><h3>&#10004;&#65039; Business confidential data</h3><ul><li><p>Internal notes</p></li><li><p>Private documents</p></li><li><p>Signed agreements</p></li></ul><h3>&#10060; When NOT to Use Encryption</h3><ul><li><p>Fields used for search or filtering (<code>LIKE</code>, <code>WHERE</code>)</p></li><li><p>Sorting fields</p></li><li><p>Non-sensitive fields</p></li></ul><p>Because encrypted data is unreadable, <strong>database-level queries become impossible</strong>.</p><div><hr></div><h2>&#11088; <strong>4. Best Practices for Using Encryption in Laravel</strong></h2><h3>&#9989; <strong>1. Always protect your APP_KEY</strong></h3><p>Never share your <code>.env</code> file<br>Never commit it to GitHub<br>Rotate keys during breaches<br>Use server-level secrets management (AWS/GCP/Vault)</p><h3>&#9989; <strong>2. Use model encryption casts</strong></h3><p>Cleaner, automatically applied, and standardized.</p><pre><code><code>protected $casts = [
   &#8216;pan_number&#8217; =&gt; &#8216;encrypted&#8217;,
];
</code></code></pre><h3>&#9989; <strong>3. Encrypt only what is necessary</strong></h3><p>Avoid performance overhead and indexing problems.</p><h3>&#9989; <strong>4. Store hash values for searchable encrypted fields</strong></h3><p>Example: store hashed email for lookup.</p><pre><code>email_encrypted
email_hash (for searching)</code></pre><h3>&#9989; <strong>5. Use Queue Encryption for Queued Jobs</strong></h3><p>Never put raw sensitive data in queues.</p><pre><code>$job-&gt;data = Crypt::encrypt($sensitive);</code></pre><h3>&#9989; <strong>6. Secure backups</strong></h3><p>Encrypted data must remain encrypted even in backups.</p><h3>&#9989; <strong>7. Don&#8217;t log sensitive information</strong></h3><p>Disable logging for encrypted sensitive data.</p><div><hr></div><h2>&#127974; <strong>Real-World Scenario: Storing Client Bank Details (EFT)</strong></h2><p>Imagine you&#8217;re building an insurance or loan management system where clients can set up their bank account for EFT payouts.</p><p>You want to store:</p><ul><li><p>Account holder name</p></li><li><p>Bank name</p></li><li><p>Account number (Sensitive &#128680;)</p></li><li><p>Routing number (Sensitive &#128680;)</p></li><li><p>Bank identifiers (IFSC / SWIFT / Branch code)</p></li></ul><p>This must be safe.</p><p>Let&#8217;s build it the secure way.</p><h3>&#129513; <strong>Step 1: Create the Migration</strong></h3><p>Here&#8217;s the migration you&#8217;ll use:</p><pre><code>Schema::create(&#8217;client_bank_details&#8217;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;unsignedBigInteger(&#8217;client_id&#8217;);

    $table-&gt;string(&#8217;account_holder_name&#8217;);
    $table-&gt;string(&#8217;bank_name&#8217;);

    // Sensitive fields - stored encrypted
    $table-&gt;text(&#8217;account_number&#8217;);
    $table-&gt;text(&#8217;routing_number&#8217;);

    $table-&gt;enum(&#8217;account_type&#8217;, [&#8217;checking&#8217;, &#8216;savings&#8217;, &#8216;business&#8217;]);
    $table-&gt;boolean(&#8217;is_primary&#8217;)-&gt;default(false);
    $table-&gt;boolean(&#8217;is_verified&#8217;)-&gt;default(false);
    $table-&gt;enum(&#8217;status&#8217;, [&#8217;active&#8217;, &#8216;inactive&#8217;])-&gt;default(&#8217;active&#8217;);

    $table-&gt;string(&#8217;branch_code&#8217;)-&gt;nullable();
    $table-&gt;string(&#8217;swift_code&#8217;)-&gt;nullable();
    $table-&gt;string(&#8217;country&#8217;)-&gt;nullable();
    $table-&gt;string(&#8217;currency&#8217;)-&gt;nullable();

    $table-&gt;json(&#8217;metadata&#8217;)-&gt;nullable();

    $table-&gt;timestamps();
});</code></pre><h3>&#128737;&#65039; <strong>Step 2: Apply Laravel Encryption Using Model Casts</strong></h3><p>This is the secret sauce.</p><p>Laravel provides a beautiful, painless way to automatically encrypt/decrypt model attributes.</p><p>Just add:</p><pre><code>protected $casts = [
    &#8216;account_number&#8217; =&gt; &#8216;encrypted&#8217;,
    &#8216;routing_number&#8217; =&gt; &#8216;encrypted&#8217;,
];</code></pre><p>Your model becomes:</p><pre><code>class ClientBankDetail extends Model
{
    protected $fillable = [
        &#8216;client_id&#8217;,
        &#8216;account_holder_name&#8217;,
        &#8216;bank_name&#8217;,
        &#8216;account_number&#8217;,
        &#8216;routing_number&#8217;,
        &#8216;account_type&#8217;,
        &#8216;is_primary&#8217;,
        &#8216;is_verified&#8217;,
        &#8216;status&#8217;,
        &#8216;branch_code&#8217;,
        &#8216;swift_code&#8217;,
        &#8216;country&#8217;,
        &#8216;currency&#8217;,
        &#8216;metadata&#8217;,
    ];

    protected $casts = [
        &#8216;account_number&#8217; =&gt; &#8216;encrypted&#8217;,
        &#8216;routing_number&#8217; =&gt; &#8216;encrypted&#8217;,
        &#8216;metadata&#8217; =&gt; &#8216;array&#8217;,
        &#8216;is_primary&#8217; =&gt; &#8216;boolean&#8217;,
        &#8216;is_verified&#8217; =&gt; &#8216;boolean&#8217;,
    ];
}</code></pre><h3>&#128640; <strong>Step 3: Storing Encrypted Bank Information</strong></h3><pre><code>ClientBankDetail::create([
    &#8216;client_id&#8217; =&gt; 101,
    &#8216;account_holder_name&#8217; =&gt; &#8216;John Doe&#8217;,
    &#8216;bank_name&#8217; =&gt; &#8216;Wells Fargo&#8217;,
    &#8216;account_number&#8217; =&gt; &#8216;9876543210&#8217;,
    &#8216;routing_number&#8217; =&gt; &#8216;021000021&#8217;,
    &#8216;account_type&#8217; =&gt; &#8216;checking&#8217;,
    &#8216;country&#8217; =&gt; &#8216;USA&#8217;,
    &#8216;currency&#8217; =&gt; &#8216;USD&#8217;
]);</code></pre><p>Laravel will:</p><ul><li><p>Encrypt the data before storing it</p></li><li><p>Decrypt it when you retrieve it</p></li><li><p>Protect it using application key (<code>APP_KEY</code>)</p></li></ul><p>All automatically.</p><h3>&#128269; <strong>Step 4: Reading Decrypted Values</strong></h3><pre><code>$bank = ClientBankDetail::find(1);
echo $bank-&gt;account_number;</code></pre><p>You get a clean value like:</p><pre><code>9876543210
</code></pre><p>But if you open the database, you see unreadable encrypted content &#8212; <strong>exactly what we want</strong>.</p><div><hr></div><h2>&#128220; Compliance &amp; Legal Benefits</h2><p>Encryption helps meet:</p><ul><li><p><strong>PCI-DSS</strong></p></li><li><p><strong>GDPR</strong></p></li><li><p><strong>SOC 2</strong></p></li></ul><p>Encryption &#8800; compliance<br>But <strong>compliance is impossible without encryption</strong>.</p><div><hr></div><h2>&#128204; Conclusion</h2><p>Laravel makes encryption incredibly simple without compromising security.</p><p>If you&#8217;re dealing with sensitive data, encryption is non-negotiable.</p><p>This blog covered:</p><p>&#10004;&#65039; What encryption is</p><p>&#10004;&#65039; How Laravel handles it</p><p>&#10004;&#65039; When to use it</p><p>&#10004;&#65039; Best practices</p><p>&#10004;&#65039; Real-world examples</p><p>&#10004;&#65039; Practical code snippets</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[🗄️Database Migrations in Laravel: The Secret Weapon for Team Productivity]]></title><description><![CDATA[How migrations prevent conflicts, streamline deployments, and boost collaboration.]]></description><link>https://newsletter.priteshbhanushali.com/p/database-migrations-in-laravel-the</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/database-migrations-in-laravel-the</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 23 Nov 2025 09:30:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!shFt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you&#8217;re building modern applications, you know that <strong>databases evolve constantly</strong> tables change, new columns get added, constraints get updated, and entire structures may require rethinking.<br>Managing this manually becomes messy, error-prone, and almost impossible when a team is involved.</p><p>This is where <strong>Database Migrations</strong> become a game-changer.</p><p>Laravel provides one of the most elegant migration systems in the industry, turning database changes into <strong>version-controlled, repeatable, trackable code</strong>.</p><p>In this article, you&#8217;ll learn:</p><ul><li><p>What a database migration is</p></li><li><p>How migrations work in Laravel</p></li><li><p>Advantages &amp; disadvantages</p></li><li><p>Best practices (with real examples)</p></li><li><p>Why migrations are essential for long-term project health</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!shFt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!shFt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!shFt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!shFt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2155196,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/179696166?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!shFt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!shFt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!shFt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd894d4bd-b09f-48d2-bb94-5aacc96bc3b8_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>&#128204; What Is a Database Migration?</h2><p>A <strong>Database Migration</strong> is simply a <strong>version control system for your database schema</strong>.</p><p>Just like Git tracks code changes, migrations track <strong>database structure changes</strong> such as:</p><ul><li><p>Creating tables</p></li><li><p>Adding or removing columns</p></li><li><p>Adding indexes or foreign keys</p></li><li><p>Changing data types</p></li><li><p>Renaming tables</p></li></ul><p>Each migration is a small PHP file that modifies the database in a controlled and reversible way.</p><div><hr></div><h2>&#128193; What Does a Migration File Look Like?</h2><p>When you run:</p><pre><code>php artisan make:migration create_users_table</code></pre><p>Laravel generates a file like this:</p><pre><code>&lt;?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create(&#8217;users&#8217;, function (Blueprint $table) {
            $table-&gt;id();
            $table-&gt;string(&#8217;name&#8217;);
            $table-&gt;string(&#8217;email&#8217;)-&gt;unique();
            $table-&gt;string(&#8217;password&#8217;);
            $table-&gt;boolean(&#8217;is_active&#8217;)-&gt;default(true);
            $table-&gt;timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists(&#8217;users&#8217;);
    }
};</code></pre><h3>&#128998; <code>up()</code></h3><p>Describes <strong>what to do</strong> &#8212; create/modify tables.</p><h3>&#128997; <code>down()</code></h3><p>Describes <strong>how to roll back</strong> the changes.</p><p>Simple. Clean. Powerful.</p><div><hr></div><h2>&#9881;&#65039; How Do Migrations Work in Laravel?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!djl3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!djl3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 424w, https://substackcdn.com/image/fetch/$s_!djl3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 848w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1272w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!djl3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png" width="1092" height="614" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:614,&quot;width&quot;:1092,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89130,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/179696166?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!djl3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 424w, https://substackcdn.com/image/fetch/$s_!djl3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 848w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1272w, https://substackcdn.com/image/fetch/$s_!djl3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F774f9a57-68b0-419b-bbef-9a280356a853_1092x614.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Laravel stores the history of executed migrations in a special table called <code>migrations</code>.</p><p>When you run:</p><pre><code>php artisan migrate</code></pre><p>Laravel:</p><ol><li><p>Reads all migration files</p></li><li><p>Checks which ones are <strong>not executed yet</strong></p></li><li><p>Runs them in order</p></li><li><p>Records them in the <code>migrations</code> table</p></li></ol><p>When you run:</p><pre><code><code>php artisan migrate:rollback</code></code></pre><p>Laravel executes the <code>down()</code> method of recent migrations.</p><p>This makes migrations <strong>safe, reversible, and manageable</strong> across environments like:</p><ul><li><p>local</p></li><li><p>staging</p></li><li><p>production</p></li></ul><div><hr></div><h2>Create Migration with Table:</h2><p>You can create a new migration by defining the table name.</p><pre><code>php artisan make:migration add_columns_to_orders_table --table=orders</code></pre><h5>&#9989; <strong>Generated Migration File</strong></h5><h5>File Name (Laravel will create something like)</h5><p><code>2025_11_23_000000_add_columns_to_orders_table.php</code><br>(The timestamp will match the moment you run the command.)</p><h5><strong>Full Migration File Content</strong></h5><pre><code>&lt;?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::table(&#8217;orders&#8217;, function (Blueprint $table) {
            //
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::table(&#8217;orders&#8217;, function (Blueprint $table) {
            //
        });
    }
};</code></pre><h5>&#128221; <strong>What this means</strong></h5><ul><li><p>Laravel <strong>will NOT guess</strong> which columns you want to add.</p></li><li><p>It simply creates an <strong>empty Schema::table(&#8217;orders&#8217;)</strong> migration.</p></li><li><p>You need to fill in the columns manually.</p></li></ul><p>Example (you will add this yourself):</p><pre><code>$table-&gt;string(&#8217;status&#8217;)-&gt;default(&#8217;pending&#8217;);
$table-&gt;date(&#8217;delivery_date&#8217;)-&gt;nullable();</code></pre><div><hr></div><h2>&#9989; Advantages of Using Migrations</h2><h4>1. <strong>Version Control for Your Database</strong></h4><p>Every schema change is stored as code.<br>No more &#8220;Did you update your DB?&#8221; chaos.</p><h4>2. <strong>Team Collaboration Becomes Easy</strong></h4><p>Developers simply pull the latest code and run:</p><pre><code><code>php artisan migrate</code></code></pre><p>Boom &#8212; database synced.</p><h4>3. <strong>Automated Deployment</strong></h4><p>CI/CD pipelines can apply migrations automatically.</p><h4>4. <strong>Rollback Support</strong></h4><p>Accidentally deployed a breaking change?<br>Rollback with one command.</p><h4>5. <strong>No More Manual SQL</strong></h4><p>You define schema changes in PHP, not raw SQL.</p><h4>6. <strong>Infrastructure Consistency</strong></h4><p>Same database structure across all environments.</p><div><hr></div><h2>&#10060; Disadvantages (Yes, They Exist)</h2><h4>1. <strong>Bad Migrations Can Break Production</strong></h4><p>If not tested properly, migrations can cause downtime.</p><h4>2. <strong>Large Tables Can Lock During Alter Operations</strong></h4><p>Adding indexes or columns to big tables may cause slowdowns.</p><h4>3. <strong>Migrations Require Discipline</strong></h4><p>Everyone in the team needs to follow the migration workflow.</p><h4>4. <strong>Rollback May Fail for Complex Logic</strong></h4><p>Some operations (like data migrations) aren&#8217;t perfectly reversible.</p><div><hr></div><h2>.&#129504; Best Practices for Laravel Migrations</h2><h4>&#10004;&#65039; 1. <strong>Never Edit a Migration That Has Already Been Run</strong></h4><p>Wrong &#10060;<br>Editing old migrations breaks other developers.</p><p>Right &#10004;&#65039;<br>Create a new migration:</p><pre><code><code>php artisan make:migration add_status_to_users_table</code></code></pre><h4>&#10004;&#65039; 2. <strong>Use Meaningful Migration Names</strong></h4><p>Good:</p><pre><code><code>2025_11_20_101010_add_status_column_to_users_table.php</code></code></pre><p>Bad:</p><pre><code><code>xyz_migration.php</code></code></pre><h4>&#10004;&#65039; 3. <strong>Keep Migrations Small and Focused</strong></h4><p>One responsibility per migration.</p><h4>&#10004;&#65039; 4. <strong>Always Provide a Rollback Method</strong></h4><p>A <code>down()</code> method should undo changes cleanly.</p><h4>&#10004;&#65039; 5. <strong>Use Database Indexes Wisely</strong></h4><p>Example:</p><pre><code><code>$table-&gt;index(&#8217;email&#8217;);</code></code></pre><p>They improve performance significantly.</p><h4>&#10004;&#65039; 6. <strong>Use </strong><code>php artisan migrate:fresh</code><strong> Only in Local Development</strong></h4><p>Never run it on staging/production &#8212; it will drop all tables.</p><h4>&#10004;&#65039; 7. <strong>Run Migrations in Transactions (If Supported)</strong></h4><p>Laravel can wrap migrations in a transaction:</p><pre><code><code>public $withinTransaction = true;</code></code></pre><p>This keeps migrations atomic.</p><div><hr></div><h2>&#127919; Benefits of Using Migrations in Real Projects</h2><ul><li><p>Faster onboarding of new developers</p></li><li><p>Zero manual DB setup</p></li><li><p>Consistent schema across all servers</p></li><li><p>Easy recovery in case of issues</p></li><li><p>Better CI/CD automation</p></li><li><p>Database and code stay in sync</p></li><li><p>Change history is preserved forever</p></li></ul><div><hr></div><h2>&#128293; Conclusion</h2><p>Database migrations are one of the most powerful features in Laravel. They turn database management into a <strong>predictable, trackable, and reversible</strong> process.</p><p>Whether you&#8217;re working solo or on a large team, migrations help ensure your application remains stable, scalable, and easier to maintain.</p><p>If you&#8217;re still editing databases manually &#8212; it&#8217;s time to level up.<br>Let your database evolve <strong>with confidence</strong> using Laravel migrations. &#128640;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 How One Laravel Mistake Can Trigger 300 Queries (and How to Fix It)]]></title><description><![CDATA[The Hidden Laravel Performance Bug: Understanding the N+1 Problem]]></description><link>https://newsletter.priteshbhanushali.com/p/how-one-laravel-mistake-can-trigger</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/how-one-laravel-mistake-can-trigger</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 16 Nov 2025 12:30:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!auPw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you work with databases and applications, you may have heard of the <strong>N+1 problem</strong>. It&#8217;s a common performance issue that happens when an application makes too many database queries instead of fetching data efficiently. This can slow down your app and make it harder to scale.</p><h2><strong>&#128269;What Is the N+1 Problem?</strong></h2><p>Imagine you have a list of <strong>blog posts</strong>, and each post has multiple <strong>comments</strong>. This is a classic one-to-many relationship because <strong>one post can have many comments</strong>.</p><p>Now, let&#8217;s say you need to fetch <strong>all posts along with their comments</strong>.<br>If your application isn&#8217;t optimized, it might do something like this:</p><h4><strong>1. First, fetch all posts:</strong></h4><pre><code>$posts = Post::all();</code></pre><h4>2. Then, for each post, fetch its comments one by one:</h4><pre><code>foreach ($posts as $post) {
    // Query for each post: SELECT * FROM comments WHERE post_id = ?
    $comments = Comment::where(&#8217;post_id&#8217;, $post-&gt;id)-&gt;get();
}</code></pre><p>If you have <strong>100 posts</strong>, this results in <strong>101 queries</strong><br>(1 query for posts + 100 queries for comments).</p><p>That&#8217;s why it&#8217;s called the <strong>N+1 problem</strong> &#8212;<br>you run <strong>N queries (one for each post) + 1 main query</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!auPw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!auPw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 424w, https://substackcdn.com/image/fetch/$s_!auPw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 848w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1272w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!auPw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png" width="808" height="620" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:808,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:404140,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/179036656?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!auPw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 424w, https://substackcdn.com/image/fetch/$s_!auPw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 848w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1272w, https://substackcdn.com/image/fetch/$s_!auPw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a4855b3-bcca-4aa9-bb98-9d7a6d0d8d57_808x620.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2>&#9888;&#65039;Why Is This a Problem?</h2><p>The N+1 problem can seriously slow down your application for several reasons:</p><ul><li><p><strong>Too many database queries overload your database server.</strong><br>Fetching each post&#8217;s comments separately forces the database to run the same query again and again.</p></li><li><p><strong>Increased network traffic between your Laravel app and the database.</strong><br>Instead of sending 2 queries (posts + all comments), your app may send 101 queries or more.</p></li><li><p><strong>Slower response times because the application waits for multiple comment queries to finish.</strong><br>Each post triggers its own query, adding delay for every request.</p></li><li><p><strong>Performance gets worse as the dataset grows.</strong><br>For example, if you have <strong>1,000 posts</strong>, Laravel may execute <strong>1,001 queries</strong><br>(1 for posts + 1 for each post&#8217;s comments).</p></li></ul><p>In short, the more posts you have, the slower your page becomes.</p><div><hr></div><h2><strong>&#129517;How to Fix the N+1 Problem</strong></h2><h3><strong>1. Use Joins to Fetch Data in One Query</strong></h3><p>Instead of making a separate query for each post&#8217;s comments, you can fetch everything in a <strong>single database query</strong> using a JOIN.</p><pre><code>SELECT posts.*, comments.*
FROM posts
LEFT JOIN comments ON posts.id = comments.post_id;</code></pre><p>This way, you avoid running 101 queries (1 for posts + 100 for comments).<br>Everything is retrieved in <strong>one efficient request</strong>.</p><p><strong>Laravel Query Builder Equivalent:</strong></p><pre><code>$posts = DB::table(&#8217;posts&#8217;)
    -&gt;leftJoin(&#8217;comments&#8217;, &#8216;posts.id&#8217;, &#8216;=&#8217;, &#8216;comments.post_id&#8217;)
    -&gt;select(&#8217;posts.*&#8217;, &#8216;comments.*&#8217;)
    -&gt;get();</code></pre><h3><strong>2. Batch Queries Instead of Fetching One by One</strong></h3><p>If you prefer not to use a JOIN, another solution is <strong>batch querying</strong>&#8212;fetching all comments for multiple posts in one call.</p><p><strong>SQL Example:</strong></p><pre><code>SELECT * FROM comments WHERE post_id IN (1, 2, 3, 4, 5);</code></pre><p>This allows you to:</p><ul><li><p>First fetch all posts</p></li><li><p>Then fetch all comments for those posts in a single batch query</p></li></ul><p>Instead of 100 separate queries, you now perform:</p><ul><li><p><strong>1 query</strong> &#8594; posts</p></li><li><p><strong>1 query</strong> &#8594; all their comments</p></li></ul><p>Total = <strong>2 queries</strong> instead of 101.</p><p><strong>Laravel Eloquent Equivalent:</strong></p><pre><code>$posts = Post::all(); // Query 1

$comments = Comment::whereIn(&#8217;post_id&#8217;, $posts-&gt;pluck(&#8217;id&#8217;))-&gt;get(); // Query 2</code></pre><h3>&#9989; The Laravel fix: Eager loading (<code>with()</code>)</h3><p>Laravel&#8217;s built-in solution is to <em>eager-load</em> relations before looping:</p><pre><code><code>// Controller (fixed)
$posts = Post::with(&#8217;comments&#8217;)-&gt;get(); // Query #1 (posts) + Query #2 (comments for all posts)

// Now in the view, no extra queries:
@foreach($posts as $post)
  @foreach($post-&gt;comments as $comment)
    {{ $comment-&gt;body }}
  @endforeach
@endforeach
</code></code></pre><p>Result: 2 queries total no matter how many posts.</p><div><hr></div><h2>&#128270; Detect N+1 in Laravel</h2><ul><li><p><strong>Laravel Debugbar</strong> (dev): shows each query and count.</p></li><li><p><strong>Laravel Telescope</strong>: records queries for requests.</p></li><li><p><strong>DB::listen()</strong>: log queries in tests or a temporary debug route.</p></li><li><p>Add assertions in tests to monitor query count (use sparingly).</p></li></ul><p>Quick example using <code>DB::listen</code>:</p><pre><code>DB::listen(function ($query) {
    logger($query-&gt;sql, $query-&gt;bindings);
});</code></pre><h2>&#128736; Handling large datasets</h2><p>Eager-loading everything for massive datasets can blow memory. Use chunking or cursors:</p><pre><code>Post::chunkById(100, function ($posts) {
    $posts-&gt;load(&#8217;comments&#8217;); // eager-load per chunk
    foreach ($posts as $post) {
        // process
    }
});</code></pre><p><code>cursor()</code> streams models but you can only eager-load per batch (collect ids, then load in batches).</p><h2>&#128064; Common gotchas &amp; pitfalls</h2><p>If you <code>select()</code> custom columns on a relation, include the foreign key so Eloquent can map relations:</p><pre><code>$posts = Post::with([&#8217;comments&#8217; =&gt; function ($q) {
    $q-&gt;select(&#8217;id&#8217;, &#8216;post_id&#8217;, &#8216;body&#8217;, &#8216;created_at&#8217;);
}])-&gt;select(&#8217;id&#8217;, &#8216;title&#8217;)-&gt;get();</code></pre><ul><li><p><code>limit()</code> inside eager load is not the same as &#8220;limit per parent&#8221; in every DB. Use <code>latestOfMany()</code> or subquery patterns for latest-N-per-parent.</p></li><li><p>Polymorphic relations require careful eager-loading; Laravel groups by type and runs efficient queries, but always test.</p></li><li><p>Over-eager-loading deep relations (<code>with(&#8217;a.b.c.d&#8217;)</code>) can increase memory and do too much work &#8212; load only what&#8217;s required.</p></li></ul><div><hr></div><h2>&#9989; Best practices checklist (senior devs use this)</h2><p>Before merging a PR, ask:</p><ul><li><p>Am I accessing a relation inside a loop? If yes, ensure that relation is eager-loaded.</p></li><li><p>Do I only need aggregates (count, sum)? Use <code>withCount()</code>, <code>withSum()</code>.</p></li><li><p>Am I selecting only the columns needed for the view/API? (Use <code>select()</code>).</p></li><li><p>Is this endpoint high-traffic? Consider caching or denormalized read models.</p></li><li><p>For large datasets, did I use chunking or streaming?</p></li><li><p>Did I profile queries with Debugbar or Telescope?</p></li><li><p>Are there joins/subqueries that could replace heavy eager-loading?</p></li></ul><div><hr></div><h2>&#127937; Final thoughts</h2><p>The N+1 problem is simple to understand and easy to fix once you know where to look. In Laravel, <code>with()</code>, <code>withCount()</code>, constrained eager loading, and smart use of joins/subqueries give you a powerful toolbox to fetch data efficiently.</p><p>If you:</p><ul><li><p>identify relations used in views,</p></li><li><p>eager-load what&#8217;s required,</p></li><li><p>prefer aggregates when possible,</p></li><li><p>and profile query counts regularly,</p></li></ul><p>you&#8217;ll keep your app fast and scalable as data grows.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🧠Understanding ACID Properties in Databases (With Laravel + MySQL Examples)]]></title><description><![CDATA[How to build reliable, consistent, and safe database operations in modern web applications]]></description><link>https://newsletter.priteshbhanushali.com/p/understanding-acid-properties-in</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/understanding-acid-properties-in</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Thu, 13 Nov 2025 04:30:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Y90z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>&#128161; Introduction: Why ACID Matters</h2><p>Every modern web application from eCommerce platforms to financial systems relies on <strong>data integrity</strong>.<br>When users transfer money, create orders, or update profiles, they expect one thing: <strong>the data should be accurate, consistent, and safe</strong>, no matter what happens in the backend.</p><p>This is where <strong>ACID properties</strong> come in.</p><p>In relational databases like <strong>MySQL</strong>, ACID ensures that each database transaction behaves predictably, even in the face of power failures, concurrency issues, or application crashes.</p><p>Laravel, with its expressive Eloquent ORM and database abstractions, gives developers easy access to these principles without needing to write low-level SQL.</p><div><hr></div><h2>&#9881;&#65039; What Does ACID Stand For?</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Y90z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Y90z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Y90z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png" width="1024" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2466999,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/178703485?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Y90z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Y90z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ebe3350-5a9c-49a1-9d34-b43e43bc92b6_1024x1536.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>ACID is an acronym for four key properties that define reliable database transactions:</p><p><strong>A &#8212; Atomicity</strong>              A transaction is all or nothing. Either all operations succeed, or none do. </p><p><strong>C &#8212; Consistency</strong>          The database must remain in a valid state before and after a transaction. </p><p><strong>I &#8212; Isolation</strong>                 Concurrent transactions should not interfere with each other. <strong>D &#8212; Durability</strong>             Once committed, data remains safe even after failures.</p><p>Let&#8217;s explore each property in detail with <strong>Laravel and MySQL examples</strong>.</p><div><hr></div><h3>&#128313; 1. Atomicity &#8212; All or Nothing</h3><p><strong>Atomicity</strong> ensures that either <em>all steps of a transaction succeed</em> or <em>none do</em>.</p><h4>&#128172; Example: Transferring Money Between Accounts</h4><p>Imagine a banking system where we transfer &#8377;100 from <strong>Account A</strong> to <strong>Account B</strong>.<br>We must <strong>decrease</strong> the balance of A and <strong>increase</strong> the balance of B in a single atomic transaction.</p><p>If the system crashes after debiting A but before crediting B &#8212; the transaction must <strong>roll back</strong>.</p><h5>&#9989; Laravel Example</h5><pre><code>use Illuminate\Support\Facades\DB;

try {
    DB::beginTransaction();

    // Debit from Account A
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 1)-&gt;decrement(&#8217;balance&#8217;, 100);

    // Credit to Account B
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 2)-&gt;increment(&#8217;balance&#8217;, 100);

    DB::commit();
} catch (\Exception $e) {
    DB::rollBack();
    Log::error(&#8217;Transaction failed: &#8216; . $e-&gt;getMessage());
}</code></pre><p>Or more elegantly:</p><pre><code>DB::transaction(function () {
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 1)-&gt;decrement(&#8217;balance&#8217;, 100);
    DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 2)-&gt;increment(&#8217;balance&#8217;, 100);
});</code></pre><p>&#128161; <strong>Best Practice:</strong><br>Use <code>DB::transaction()</code> whenever you perform multiple related writes that must succeed or fail together.</p><div><hr></div><h3>&#128313; 2. Consistency &#8212; Maintaining Data Integrity</h3><p><strong>Consistency</strong> ensures that the database transitions from one valid state to another.</p><p>This is often maintained using <strong>constraints</strong>, <strong>foreign keys</strong>, <strong>validation</strong>, and <strong>business logic</strong>.</p><h4>&#128172; Example: Creating an Order and Order Items</h4><p>If you create an order but fail to insert the related order items, your database becomes inconsistent.</p><h5>&#9989; Laravel Example</h5><pre><code>DB::transaction(function () {
    $order = Order::create([
        &#8216;user_id&#8217; =&gt; 1,
        &#8216;total&#8217; =&gt; 500,
    ]);

    OrderItem::create([
        &#8216;order_id&#8217; =&gt; $order-&gt;id,
        &#8216;product_id&#8217; =&gt; 42,
        &#8216;quantity&#8217; =&gt; 2,
        &#8216;price&#8217; =&gt; 250,
    ]);
});</code></pre><h4>&#128161; Database-Level Consistency</h4><p>At the schema level, we ensure consistency with <strong>foreign key constraints</strong> and <strong>unique indexes</strong>:</p><pre><code>Schema::create(&#8217;order_items&#8217;, function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;foreignId(&#8217;order_id&#8217;)-&gt;constrained()-&gt;onDelete(&#8217;cascade&#8217;);
    $table-&gt;foreignId(&#8217;product_id&#8217;)-&gt;constrained();
    $table-&gt;integer(&#8217;quantity&#8217;);
    $table-&gt;decimal(&#8217;price&#8217;, 10, 2);
});</code></pre><p>If someone tries to insert an order item with an invalid <code>order_id</code>, MySQL will reject it maintaining consistency.</p><div><hr></div><h3>&#128313; 3. Isolation &#8212; Safe Concurrent Transactions</h3><p><strong>Isolation</strong> means that concurrent transactions don&#8217;t affect each other&#8217;s results.</p><p>MySQL supports multiple <strong>isolation levels</strong>:</p><p><strong>READ UNCOMMITTED               </strong>Can read uncommitted data (dirty reads).</p><p><strong>READ COMMITTED                      </strong>Reads only committed data.</p><p><strong>REPEATABLE READ</strong> <em>(default in MySQL)     </em>Same result for repeated reads within a transaction.</p><p><strong>SERIALIZABLE                            </strong>Highest isolation; transactions run one after another.</p><h4>&#128172; Example: Preventing Double Spending</h4><p>Two users try to withdraw from the same account simultaneously.<br>Isolation ensures that one transaction waits until the other completes.</p><h5>&#9989; Laravel Example</h5><pre><code>DB::transaction(function () {
    $account = DB::table(&#8217;accounts&#8217;)
        -&gt;where(&#8217;id&#8217;, 1)
        -&gt;lockForUpdate()
        -&gt;first();

    if ($account-&gt;balance &gt;= 100) {
        DB::table(&#8217;accounts&#8217;)-&gt;where(&#8217;id&#8217;, 1)-&gt;decrement(&#8217;balance&#8217;, 100);
    } else {
        throw new \Exception(&#8217;Insufficient balance.&#8217;);
    }
});</code></pre><p><code>lockForUpdate()</code> prevents other transactions from reading or updating the same row until this one finishes  ensuring <strong>isolation</strong>.</p><div><hr></div><h3>&#128313; 4. Durability &#8212; Data That Stays</h3><p><strong>Durability</strong> ensures that once a transaction is committed, it will remain saved even if a power failure or crash occurs.</p><p>In MySQL&#8217;s <strong>InnoDB engine</strong>, durability is maintained using:</p><ul><li><p><strong>Redo logs (write-ahead logs)</strong></p></li><li><p><strong>Automatic crash recovery</strong></p></li><li><p><strong>fsync() writes to disk after commit</strong></p></li></ul><h4>&#128172; Laravel&#8217;s Role</h4><p>Laravel delegates durability to MySQL &#8212; but you can ensure safer commits by:</p><ul><li><p>Using <code>DB::transaction()</code> to avoid half-committed writes</p></li><li><p>Configuring <strong>InnoDB</strong> as the storage engine</p></li><li><p>Setting proper <code>innodb_flush_log_at_trx_commit</code> (default <code>1</code> ensures maximum durability)</p></li></ul><div><hr></div><h3>&#129513; Putting It All Together: A Real-World Scenario</h3><h4>Scenario: Order Creation with Payment Logging</h4><pre><code>DB::transaction(function () use ($request) {
    $order = Order::create([
        &#8216;user_id&#8217; =&gt; $request-&gt;user_id,
        &#8216;total&#8217; =&gt; $request-&gt;total,
    ]);

    foreach ($request-&gt;items as $item) {
        OrderItem::create([
            &#8216;order_id&#8217; =&gt; $order-&gt;id,
            &#8216;product_id&#8217; =&gt; $item[&#8217;product_id&#8217;],
            &#8216;quantity&#8217; =&gt; $item[&#8217;qty&#8217;],
            &#8216;price&#8217; =&gt; $item[&#8217;price&#8217;],
        ]);
    }

    Payment::create([
        &#8216;order_id&#8217; =&gt; $order-&gt;id,
        &#8216;amount&#8217; =&gt; $order-&gt;total,
        &#8216;status&#8217; =&gt; &#8216;SUCCESS&#8217;,
    ]);
});</code></pre><p>If any insert fails, Laravel automatically rolls back ensuring <strong>Atomicity</strong>, <strong>Consistency</strong>, and <strong>Durability</strong>.<br>MySQL&#8217;s <strong>transaction isolation</strong> ensures that other users see consistent data while this operation is in progress.</p><div><hr></div><h3>&#129517; Best Practices for Handling Transactions in Laravel</h3><ol><li><p>&#9989; <strong>Use </strong><code>DB::transaction()</code> for multi-step operations.</p></li><li><p>&#128257; <strong>Handle Exceptions Gracefully</strong> - rollback on failure and log errors.</p></li><li><p>&#129521; <strong>Leverage Database Constraints</strong> (foreign keys, unique keys).</p></li><li><p>&#9881;&#65039; <strong>Tune Isolation Levels</strong> - use <code>REPEATABLE READ</code> for safety, or <code>READ COMMITTED</code> for performance.</p></li><li><p>&#129513; <strong>Keep Transactions Short</strong> - long-running transactions can cause locks and deadlocks.</p></li><li><p>&#128190; <strong>Always Use InnoDB</strong> - for full ACID compliance.</p></li></ol><div><hr></div><h3>&#129534; Summary &amp; Key Takeaways</h3><ul><li><p><strong>Atomicity</strong> ensures operations succeed together.</p></li><li><p><strong>Consistency</strong> keeps your data valid and rules enforced.</p></li><li><p><strong>Isolation</strong> prevents race conditions in concurrent access.</p></li><li><p><strong>Durability</strong> guarantees data safety after commits.</p></li></ul><p>When combined, these principles make your <strong>Laravel + MySQL applications reliable, predictable, and production-safe</strong>.</p><p>Building with ACID in mind means your users can trust your app - every time they hit &#8220;Submit&#8221;.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushalis newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🧠 Subquery vs CTE vs Temporary Table — The Ultimate Comparison with Practical Examples]]></title><description><![CDATA[Understand When and Why to Use Each for Better Query Performance]]></description><link>https://newsletter.priteshbhanushali.com/p/subquery-vs-cte-vs-temporary-table</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/subquery-vs-cte-vs-temporary-table</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Thu, 06 Nov 2025 04:30:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q1X0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>When writing SQL queries, choosing between a Subquery, CTE, or Temporary Table can make or break your query&#8217;s performance and readability.</strong><br>Though they all help you break complex problems into smaller chunks, each has its own strengths and ideal use cases.</p><p>In this post, we&#8217;ll explore:</p><ul><li><p>What each of them is</p></li><li><p>How they differ</p></li><li><p>Practical SQL examples</p></li><li><p>When to use which one</p></li></ul><div><hr></div><h3>&#128313; 1. What is a Subquery?</h3><p>A <strong>subquery</strong> (or <em>nested query</em>) is a query inside another query.<br>It can appear in the <code>SELECT</code>, <code>FROM</code>, or <code>WHERE</code> clause and is used when you need to use the result of one query inside another.</p><h4>&#129513; Example:</h4><p>Let&#8217;s say you want to fetch all employees who work in the <strong>IT department</strong>.</p><pre><code>SELECT first_name, last_name
FROM employees
WHERE department_id = (
    SELECT department_id
    FROM departments
    WHERE department_name = &#8216;IT&#8217;
);</code></pre><p>Here, the inner query:</p><pre><code>SELECT department_id FROM departments WHERE department_name = &#8216;IT&#8217;;</code></pre><p>runs first, and its result is used by the outer query.</p><h4>&#9989; When to Use:</h4><ul><li><p>For simple filtering or lookups.</p></li><li><p>When you need the result only once.</p></li></ul><h4>&#9888;&#65039; Be Careful:</h4><ul><li><p>Can become <strong>slow</strong> if used inside loops or complex joins.</p></li><li><p>Not ideal for <strong>reusing</strong> results.</p></li></ul><div><hr></div><h3>&#128313; 2. What is a CTE (Common Table Expression)?</h3><p>A <strong>CTE</strong> is a temporary named result set that exists only during the execution of a single SQL statement.<br>Think of it as a <strong>named subquery</strong> that improves readability and maintainability.</p><p>It starts with the <code>WITH</code> keyword.</p><h4>&#129513; Example:</h4><pre><code>WITH IT_Employees AS (
    SELECT employee_id, first_name, last_name, department_id
    FROM employees
    WHERE department_id = (
        SELECT department_id FROM departments WHERE department_name = &#8216;IT&#8217;
    )
)
SELECT first_name, last_name
FROM IT_Employees
WHERE last_name LIKE &#8216;S%&#8217;;</code></pre><p>Here, the CTE <code>IT_Employees</code> holds the intermediate result which we later query again.</p><h4>&#9989; Benefits:</h4><ul><li><p><strong>Readable</strong> and <strong>modular</strong> &#8212; perfect for long, multi-step queries.</p></li><li><p>Can be <strong>recursive</strong>, great for hierarchical data (like org charts).</p></li></ul><h4>&#9888;&#65039; Limitations:</h4><ul><li><p>Exists only within that query.</p></li><li><p>Cannot be indexed or reused across multiple queries.</p></li></ul><div><hr></div><h3>&#128313; 3. What is a Temporary Table?</h3><p>A <strong>Temporary Table</strong> is like a normal table, but it&#8217;s stored temporarily in your database (e.g., in <code>tempdb</code> in SQL Server).<br>You can <strong>create</strong>, <strong>index</strong>, <strong>update</strong>, and <strong>reuse</strong> it across multiple queries within the same session.</p><h4>&#129513; Example:</h4><pre><code>CREATE TEMPORARY TABLE temp_it_employees AS
SELECT employee_id, first_name, last_name, department_id
FROM employees
WHERE department_id = (
    SELECT department_id FROM departments WHERE department_name = &#8216;IT&#8217;
);

SELECT * FROM temp_it_employees WHERE last_name LIKE &#8216;S%&#8217;;</code></pre><p>You can even index it for faster performance:</p><pre><code>CREATE INDEX idx_lastname ON temp_it_employees(last_name);</code></pre><h4>&#9989; Benefits:</h4><ul><li><p>Can be reused multiple times.</p></li><li><p>Supports indexing &#8212; improves performance on large datasets.</p></li><li><p>Ideal for <strong>complex transformations</strong> or <strong>multi-step logic</strong>.</p></li></ul><h4>&#9888;&#65039; Limitations:</h4><ul><li><p>Needs manual creation and cleanup.</p></li><li><p>Slight overhead for disk/memory usage.</p></li></ul><div><hr></div><h3>&#128312; 4. Comparison Summary</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q1X0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q1X0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 424w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 848w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1272w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!q1X0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png" width="1042" height="453" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:453,&quot;width&quot;:1042,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41525,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177801281?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q1X0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 424w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 848w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1272w, https://substackcdn.com/image/fetch/$s_!q1X0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca45d193-8dff-475e-9e19-7efa4f2d8aaf_1042x453.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#129504; 5. Real-World Analogy</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nQ-k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nQ-k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 424w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 848w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1272w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png" width="969" height="255" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:255,&quot;width&quot;:969,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:21789,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177801281?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nQ-k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 424w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 848w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1272w, https://substackcdn.com/image/fetch/$s_!nQ-k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F22955a9a-e68b-4bd8-af01-7554b10533a1_969x255.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128313; 6. Practical Performance Example</h3><p>Let&#8217;s consider a real-world scenario:</p><blockquote><p>You need to find all employees who worked more than 200 hours in the past 3 months and belong to departments with average salary &gt; 80,000.</p></blockquote><p>You can do it in <strong>three ways</strong>:</p><h4>&#128312; Subquery:</h4><pre><code>SELECT e.employee_id, e.first_name, e.last_name
FROM employees e
WHERE e.department_id IN (
    SELECT department_id FROM departments WHERE avg_salary &gt; 80000
)
AND e.employee_id IN (
    SELECT employee_id FROM work_hours
    WHERE total_hours &gt; 200
);</code></pre><h4>&#128312; CTE:</h4><pre><code>WITH HighSalaryDepartments AS (
    SELECT department_id FROM departments WHERE avg_salary &gt; 80000
),
ActiveEmployees AS (
    SELECT employee_id FROM work_hours WHERE total_hours &gt; 200
)
SELECT e.employee_id, e.first_name, e.last_name
FROM employees e
JOIN HighSalaryDepartments d ON e.department_id = d.department_id
JOIN ActiveEmployees a ON e.employee_id = a.employee_id;</code></pre><h4>&#128312; Temporary Table:</h4><pre><code>CREATE TEMPORARY TABLE high_salary_departments AS
SELECT department_id FROM departments WHERE avg_salary &gt; 80000;

CREATE TEMPORARY TABLE active_employees AS
SELECT employee_id FROM work_hours WHERE total_hours &gt; 200;

SELECT e.employee_id, e.first_name, e.last_name
FROM employees e
JOIN high_salary_departments d ON e.department_id = d.department_id
JOIN active_employees a ON e.employee_id = a.employee_id;</code></pre><p>&#128073; In this example:</p><ul><li><p><strong>Subquery</strong> works fine for small datasets.</p></li><li><p><strong>CTE</strong> is cleaner and easier to maintain.</p></li><li><p><strong>Temporary Table</strong> gives the best performance for large datasets or repeated queries.</p></li></ul><div><hr></div><h3>&#128313; 7. Best Practices</h3><p>&#9989; <strong>Use Subqueries</strong> for quick and simple conditions.<br>&#9989; <strong>Use CTEs</strong> for readable, modular logic and recursive problems.<br>&#9989; <strong>Use Temporary Tables</strong> for large or reusable intermediate data sets.<br>&#9989; Always test performance with <code>EXPLAIN</code> or query plans.<br>&#9989; Clean up temporary tables if not automatically dropped.</p><div><hr></div><h3>&#128161; Final Thoughts</h3><p>Choosing between a <strong>Subquery</strong>, <strong>CTE</strong>, and <strong>Temporary Table</strong> depends on <strong>what you value most</strong> &#8212; readability, performance, or reusability.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cB_7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 424w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 848w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1272w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!cB_7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png" width="846" height="225" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:225,&quot;width&quot;:846,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9982,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177801281?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cB_7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 424w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 848w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1272w, https://substackcdn.com/image/fetch/$s_!cB_7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522d14cd-11c2-4d84-9617-0c1ebc3d1db0_846x225.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>Remember &#8212; <strong>clean SQL is not just about running faster, but also about being maintainable</strong>.<br>Start small, measure performance, and evolve your query structure as your data grows.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[🚀 MySQL UNION vs UNION ALL - Key Differences and When to Use Each]]></title><description><![CDATA[Learn how these two simple keywords can make or break your SQL performance]]></description><link>https://newsletter.priteshbhanushali.com/p/mysql-union-vs-union-all-key-differences</link><guid isPermaLink="false">https://newsletter.priteshbhanushali.com/p/mysql-union-vs-union-all-key-differences</guid><dc:creator><![CDATA[Pritesh Bhanushali]]></dc:creator><pubDate>Sun, 02 Nov 2025 10:06:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!s-T8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When working with SQL queries, we often need to combine results from multiple tables or queries into one unified dataset.<br>In MySQL, this is where <code>UNION</code> and <code>UNION ALL</code> come in handy.</p><p>Although they look similar, they behave differently under the hood and have performance implications that every developer should understand.</p><div><hr></div><h3>&#9881;&#65039; What is <code>UNION</code>?</h3><p>The <code>UNION</code> operator <strong>combines the results of two or more SELECT statements</strong> and <strong>removes duplicate rows</strong> from the final result set.</p><h4>&#9989; Syntax:</h4><pre><code>SELECT column_list FROM table1
UNION
SELECT column_list FROM table2;</code></pre><h3>&#128269; Example:</h3><p>Let&#8217;s say we have two tables &#8212; <code>employees_2024</code> and <code>employees_2025</code>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s-T8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s-T8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 424w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 848w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1272w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!s-T8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png" width="875" height="452" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:452,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16145,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s-T8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 424w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 848w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1272w, https://substackcdn.com/image/fetch/$s_!s-T8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c9b66a9-451e-4773-85d3-a9041db2bdab_875x452.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Now, if we run:</p><pre><code>SELECT name FROM employees_2024
UNION
SELECT name FROM employees_2025;</code></pre><h4>&#129534; Output:</h4><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ktVQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 424w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 848w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1272w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png" width="723" height="278.3611584327087" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:226,&quot;width&quot;:587,&quot;resizeWidth&quot;:723,&quot;bytes&quot;:5142,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ktVQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 424w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 848w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1272w, https://substackcdn.com/image/fetch/$s_!ktVQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe21ed833-6dbf-4737-babd-48d9187ae9e4_587x226.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>&#10145;&#65039; Notice how <strong>&#8220;Alice&#8221; appears only once</strong> &#8212; duplicates are removed automatically.</p><div><hr></div><h3>&#9889; What is <code>UNION ALL</code>?</h3><p>The <code>UNION ALL</code> operator also combines the results of multiple <code>SELECT</code> statements &#8212;<br><strong>but it does not remove duplicates.</strong></p><h4>&#9989; Syntax:</h4><pre><code>SELECT column_list FROM table1
UNION ALL
SELECT column_list FROM table2;</code></pre><h3>&#128269; Example:</h3><p>Using the same tables:</p><pre><code>SELECT name FROM employees_2024
UNION ALL
SELECT name FROM employees_2025;</code></pre><h4>&#129534; Output:</h4><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jkOW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jkOW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 424w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 848w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1272w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!jkOW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png" width="829" height="246" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:246,&quot;width&quot;:829,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6840,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jkOW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 424w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 848w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1272w, https://substackcdn.com/image/fetch/$s_!jkOW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6cdb0ceb-31c6-4552-9226-aedfbf99948c_829x246.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#10145;&#65039; Here, <strong>&#8220;Alice&#8221; appears twice</strong> &#8212; once from each table.</p><div><hr></div><h3>&#9878;&#65039; Key Differences Between UNION and UNION ALL</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IxEZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IxEZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 424w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 848w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1272w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png" width="624" height="708" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:708,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:65039,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IxEZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 424w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 848w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1272w, https://substackcdn.com/image/fetch/$s_!IxEZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff2220224-ddce-42b9-af62-57dfa606b1c4_624x708.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h3>&#128640; Performance Example</h3><p>Let&#8217;s run a quick comparison with a large dataset.</p><pre><code>-- UNION
SELECT name FROM sales_2023
UNION
SELECT name FROM sales_2024;

-- UNION ALL
SELECT name FROM sales_2023
UNION ALL
SELECT name FROM sales_2024;</code></pre><h3>&#9881;&#65039; Behind the scenes:</h3><ul><li><p><code>UNION</code> performs an <strong>implicit </strong><code>DISTINCT</code> operation &#8212; MySQL creates a temporary table, sorts results, and removes duplicates.</p></li><li><p><code>UNION ALL</code> simply <strong>concatenates results</strong>, making it much faster for large data sets.</p></li></ul><p><strong>Rule of thumb:</strong><br>If you don&#8217;t need to eliminate duplicates &#8594; <strong>always prefer </strong><code>UNION ALL</code> for better performance.</p><div><hr></div><h3>&#127757; Real-World Examples</h3><h4>&#127970; Example 1: Combining Customer Lists from Multiple Branches</h4><p>Let&#8217;s say you have separate databases or tables for different store branches:</p><ul><li><p><code>customers_mumbai</code></p></li><li><p><code>customers_delhi</code></p></li></ul><p>Each table stores customer names. Some customers shop in both branches.</p><h5>&#9989; Using UNION (to get unique customers)</h5><pre><code>SELECT customer_name FROM customers_mumbai
UNION
SELECT customer_name FROM customers_delhi;</code></pre><h5>Output:</h5><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LKCL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 424w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 848w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1272w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!LKCL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png" width="728" height="254.687306501548" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:226,&quot;width&quot;:646,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:6644,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LKCL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 424w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 848w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1272w, https://substackcdn.com/image/fetch/$s_!LKCL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f842b75-4814-44b6-ad3d-41e1ca9652db_646x226.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>&#10145;&#65039; This gives you a <strong>unique list</strong> of customers across both branches &#8212; useful for CRM or marketing campaigns where duplicates are not needed.</p><h5>&#9889; Using UNION ALL (to track total visits)</h5><pre><code>SELECT customer_name FROM customers_mumbai
UNION ALL
SELECT customer_name FROM customers_delhi;</code></pre><h5>Output:</h5><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iiTu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iiTu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 424w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 848w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1272w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!iiTu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png" width="796" height="265" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:265,&quot;width&quot;:796,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8233,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iiTu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 424w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 848w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1272w, https://substackcdn.com/image/fetch/$s_!iiTu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F97b99254-3c60-4612-b17b-d6ab545f821c_796x265.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#10145;&#65039; This shows <strong>each occurrence</strong>, meaning Priya visited both branches &#8212; helpful when you want to <strong>count total visits or analyze activity</strong>.</p><div><hr></div><h3>&#128161; When to Use Which?</h3><h4>&#9989; Use <code>UNION</code> When:</h4><ul><li><p>You need <strong>unique results</strong> only.<br>Example: Combining customer lists from multiple regions.</p></li></ul><h4>&#9989; Use <code>UNION ALL</code> When:</h4><ul><li><p>You&#8217;re sure data won&#8217;t overlap or duplicates are acceptable.<br>Example: Merging monthly logs or combining partitions.</p></li></ul><div><hr></div><h3>&#9888;&#65039; Common Mistakes to Avoid</h3><ol><li><p><strong>Mismatched columns:</strong><br>Each SELECT must return the <strong>same number of columns</strong> with <strong>compatible data types</strong>.</p></li></ol><pre><code>-- &#10060; This will fail
SELECT id, name FROM table1
UNION
SELECT name FROM table2;</code></pre><ol start="2"><li><p><strong>Expecting automatic sorting:</strong><br>The result of <code>UNION</code> or <code>UNION ALL</code> is <strong>not guaranteed to be ordered</strong>.<br>Use <code>ORDER BY</code> after the last query:</p></li></ol><pre><code>SELECT name FROM table1
UNION ALL
SELECT name FROM table2
ORDER BY name;</code></pre><div><hr></div><h4>&#129504; Pro Tip</h4><p>If you need to count duplicates or analyze overlaps, you can simulate both behaviors:</p><pre><code>SELECT name, COUNT(*) AS occurrence
FROM (
  SELECT name FROM employees_2024
  UNION ALL
  SELECT name FROM employees_2025
) AS combined
GROUP BY name;</code></pre><p>This helps detect duplicates even when using <code>UNION ALL</code>.</p><div><hr></div><h2>&#127937; Conclusion</h2><p>Both <code>UNION</code> and <code>UNION ALL</code> are powerful tools in MySQL for combining data from multiple queries &#8212; but understanding their differences is essential for writing efficient SQL.</p><ul><li><p><code>UNION</code> gives you a <em>clean, unique</em> result set by removing duplicates &#8212; ideal for cases where you care about distinct data (like unique customers, leads, or products).</p></li><li><p><code>UNION ALL</code>, on the other hand, is <em>faster and lighter</em> because it doesn&#8217;t check for duplicates &#8212; making it perfect for analytics, reporting, and log data where every record matters.</p></li></ul><p>In short:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HEJp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 424w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 848w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1272w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!HEJp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png" width="882" height="155" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:155,&quot;width&quot;:882,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9496,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://newsletter.priteshbhanushali.com/i/177779984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HEJp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 424w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 848w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1272w, https://substackcdn.com/image/fetch/$s_!HEJp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c62d84b-da98-4868-8388-ed9fe986d4e6_882x155.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>Think of it this way &#8212;</p><blockquote><p>Use <code>UNION</code> for <em>accuracy</em>, and <code>UNION ALL</code> for <em>speed</em>.</p></blockquote><p>The best SQL developers know when to choose between the two, balancing <strong>data correctness</strong> and <strong>query performance</strong> based on the context.</p><p>So next time you merge data across tables, pause for a second and ask yourself &#8212;<br><strong>Do I really need to remove duplicates?</strong><br>If not, give <code>UNION ALL</code> the green light. &#128678;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://newsletter.priteshbhanushali.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Pritesh Bhanushali&#8217;s newsletter! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item></channel></rss>