<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: longtk26</title>
    <description>The latest articles on DEV Community by longtk26 (@longtk26).</description>
    <link>https://dev.to/longtk26</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F915607%2F896c224d-6196-4cd9-93a2-a51507438d28.png</url>
      <title>DEV Community: longtk26</title>
      <link>https://dev.to/longtk26</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/longtk26"/>
    <language>en</language>
    <item>
      <title>Kubernetes Architecture and Primitives: From Containers to Orchestration</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Sat, 31 Oct 2026 08:00:35 +0000</pubDate>
      <link>https://dev.to/longtk26/kubernetes-architecture-and-primitives-from-containers-to-orchestration-3n15</link>
      <guid>https://dev.to/longtk26/kubernetes-architecture-and-primitives-from-containers-to-orchestration-3n15</guid>
      <description>&lt;p&gt;Running containers with standalone Docker commands works well on a single machine, but operating a container fleet across multiple hosts introduces critical operational challenges: recovering from container crashes, executing zero-downtime rollouts, and dynamically routing network traffic. Kubernetes solves this by operating as a declarative orchestration system that continuously reconciles the cluster's actual state with your defined desired state. Through its centralized &lt;a href="https://kubernetes.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;cluster architecture&lt;/a&gt;, Kubernetes delegates administrative control to a dedicated control plane while worker nodes manage container execution and health monitoring.&lt;/p&gt;

&lt;p&gt;In this guide, we break down Kubernetes architecture from first principles to transition your mental model from isolated Docker containers to automated orchestration. We examine how the control plane exposes its API through the API server, uses the scheduler to place unassigned Pods onto suitable nodes, and packages control loops into the controller manager. We then trace how worker node components—specifically the kubelet ensuring container health and kube-proxy maintaining network rules—execute workloads, before analyzing workload hierarchies from Pods to Deployments, stable service networking, and automated health checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
Why Kubernetes? From Manual Docker to Automated Orchestration

&lt;ul&gt;
&lt;li&gt;The Operational Limits of Standalone Docker&lt;/li&gt;
&lt;li&gt;Declarative Reconciliation vs. Imperative Management&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Cluster Architecture: Control Plane and Worker Nodes

&lt;ul&gt;
&lt;li&gt;The Control Plane: The Cluster Brain&lt;/li&gt;
&lt;li&gt;Worker Nodes: The Execution Fleet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
The Workload Hierarchy: Pods, ReplicaSets, and Deployments

&lt;ul&gt;
&lt;li&gt;1. The Pod: The Atomic Unit of Scheduling&lt;/li&gt;
&lt;li&gt;2. ReplicaSets: Declarative Self-Healing and Scale&lt;/li&gt;
&lt;li&gt;3. Deployments: Declarative Rolling Updates and Rollbacks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Service Networking: Stable Virtual IPs and Traffic Distribution

&lt;ul&gt;
&lt;li&gt;The Service Primitive and EndpointSlices&lt;/li&gt;
&lt;li&gt;How kube-proxy Routes Traffic&lt;/li&gt;
&lt;li&gt;Service Types and Exposure Scope&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Health Probes: Startup, Liveness, and Readiness

&lt;ul&gt;
&lt;li&gt;The Three Probe Types&lt;/li&gt;
&lt;li&gt;1. Startup Probes: Guarding Slow Initialization&lt;/li&gt;
&lt;li&gt;2. Liveness Probes: Self-Healing Deadlocks&lt;/li&gt;
&lt;li&gt;3. Readiness Probes: Dynamic Traffic Gating&lt;/li&gt;
&lt;li&gt;Probe Configuration and the Cascading Restart Trap&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Auxiliary Primitives and the End-to-End Request Lifecycle

&lt;ul&gt;
&lt;li&gt;Supporting Primitives: Configuration, Isolation, and Quotas&lt;/li&gt;
&lt;li&gt;Ingress and the End-to-End Request Path&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Summary &amp;amp; What's Next

&lt;ul&gt;
&lt;li&gt;Core Primitives Summary&lt;/li&gt;
&lt;li&gt;What's Next: Hands-On Practice&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Why Kubernetes? From Manual Docker to Automated Orchestration
&lt;/h2&gt;

&lt;p&gt;Docker packages an application and its runtime dependencies into a reproducible, isolated container. Running &lt;code&gt;docker run&lt;/code&gt; on a local workstation or a single virtual machine provides a reliable developer experience, but managing standalone Docker containers in production quickly runs into severe operational walls. To use an analogy: &lt;strong&gt;Docker is a single musical instrument; Kubernetes is the conductor coordinating the entire symphony&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Operational Limits of Standalone Docker
&lt;/h3&gt;

&lt;p&gt;When deploying distributed microservices across multiple physical or virtual hosts, manual container operations fail at scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Host failures and downtime:&lt;/strong&gt; If a physical server hosting a Docker daemon crashes, all containers on that host terminate immediately. Standalone Docker cannot detect external node failures or automatically resurrect containers on healthy hardware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bespoke operational tooling:&lt;/strong&gt; Teams managing raw containers inevitably build fragile maintenance layers—custom shell scripts, external cron jobs, and host-level systemd units—to inspect health statuses, trigger restarts, and map ephemeral container ports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual scaling bottlenecks:&lt;/strong&gt; Handling sudden traffic spikes requires operators to provision new VMs, run manual deployment commands, and reconfigure reverse proxies or load balancers with the new host IP addresses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Declarative Reconciliation vs. Imperative Management
&lt;/h3&gt;

&lt;p&gt;Kubernetes fundamentally shifts operations from an imperative model ("run this container now") to a &lt;strong&gt;declarative reconciliation model&lt;/strong&gt; ("maintain three healthy replicas of this workload").&lt;/p&gt;

&lt;p&gt;Rather than executing continuous shell commands to keep services online, you declare your desired system state in version-controlled manifests. As outlined in &lt;a href="https://k8s.info/docs/foundations/architecture" rel="noopener noreferrer"&gt;The Kubernetes Visual Handbook&lt;/a&gt;, Kubernetes operates via an infinite reconciliation loop where controllers constantly observe actual state, compare it against desired state, and act to eliminate any discrepancies.&lt;/p&gt;

&lt;p&gt;Furthermore, &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;Google Cloud's GKE cluster architecture documentation&lt;/a&gt; highlights that the control plane actively schedules workloads onto nodes while managing workload lifecycles, scaling, networking, and storage. If a worker node crashes or an application process fails its health checks, the control plane immediately detects the deficit and reschedules replacement workloads across the remaining cluster pool without human intervention.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operational Dimension&lt;/th&gt;
&lt;th&gt;Standalone Docker&lt;/th&gt;
&lt;th&gt;Kubernetes Orchestration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Management Paradigm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Imperative commands (&lt;code&gt;docker run&lt;/code&gt;, &lt;code&gt;docker restart&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Declarative state reconciliation loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure Recovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual intervention or custom watchdog scripts&lt;/td&gt;
&lt;td&gt;Automated self-healing and workload rescheduling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-Host Scheduling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None (bound to a single Docker host)&lt;/td&gt;
&lt;td&gt;Native scheduling based on resource requests and node capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Discovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Static port forwarding and manual proxy updates&lt;/td&gt;
&lt;td&gt;Cluster-internal DNS and dynamic virtual IPs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Disruptive manual container replacement&lt;/td&gt;
&lt;td&gt;Automated, zero-downtime rolling updates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2. Cluster Architecture: Control Plane and Worker Nodes
&lt;/h2&gt;

&lt;p&gt;To transition from operating standalone Docker daemons to managing distributed infrastructure, we must first examine how Kubernetes decouples administrative orchestration from container execution. As detailed in the &lt;a href="https://kubernetes.io/docs/concepts/overview/components" rel="noopener noreferrer"&gt;Kubernetes components documentation&lt;/a&gt;, a cluster is split into two primary operational tiers: the &lt;strong&gt;control plane&lt;/strong&gt; and one or more &lt;strong&gt;worker nodes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The control plane makes global decisions about the cluster (such as scheduling workloads and responding to node failures), while worker nodes supply the actual compute capacity to run your containerized workloads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyy87pqrggxze9e8ztzsw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyy87pqrggxze9e8ztzsw.png" alt="Kubernetes Cluster Architecture: Control Plane and Worker Nodes" width="798" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Control Plane: The Cluster Brain
&lt;/h3&gt;

&lt;p&gt;The control plane maintains the desired state of all workloads across your machines, acting as the decision-making engine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kube-apiserver&lt;/code&gt;:&lt;/strong&gt; The stateless entry point and central communication hub for the entire cluster. According to &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;Google Cloud's GKE cluster architecture guide&lt;/a&gt;, all internal components and external client requests (&lt;code&gt;kubectl&lt;/code&gt;) interact strictly through this REST API. It handles authentication, authorization, validation, and admission control. Crucially, it is the &lt;strong&gt;only component that directly reads from or writes to &lt;code&gt;etcd&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;etcd&lt;/code&gt;:&lt;/strong&gt; A strongly consistent, distributed key-value store that acts as the cluster's single source of truth. As outlined in the &lt;a href="https://kubernetes.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;Kubernetes architecture documentation&lt;/a&gt;, &lt;code&gt;etcd&lt;/code&gt; holds all cluster state, resource specifications, and runtime metadata. Because &lt;code&gt;kube-apiserver&lt;/code&gt; is stateless, multiple API server replicas can run behind a load balancer while sharing this common datastore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kube-scheduler&lt;/code&gt;:&lt;/strong&gt; When you submit a Pod manifest without a designated node assignment, the scheduler assigns it to a suitable machine. As described in &lt;a href="https://k8s.info/docs/foundations/architecture" rel="noopener noreferrer"&gt;The Kubernetes Visual Handbook&lt;/a&gt;, the scheduler evaluates nodes through &lt;strong&gt;filtering&lt;/strong&gt; (eliminating nodes that do not meet CPU, memory, or policy constraints) and &lt;strong&gt;scoring&lt;/strong&gt; (ranking remaining nodes to pick the optimal candidate).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kube-controller-manager&lt;/code&gt;:&lt;/strong&gt; A single binary combining multiple control loops that continuously compare the current state of the cluster with the desired state stored in &lt;code&gt;etcd&lt;/code&gt;, issuing corrections whenever drift occurs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Worker Nodes: The Execution Fleet
&lt;/h3&gt;

&lt;p&gt;While the control plane handles coordination, worker nodes provide the compute infrastructure where user workloads execute. As highlighted in &lt;a href="https://devopscube.com/kubernetes-architecture-explained" rel="noopener noreferrer"&gt;DevOpsCube's Kubernetes architecture guide&lt;/a&gt;, each worker machine hosts three primary elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kubelet&lt;/code&gt;:&lt;/strong&gt; The primary node agent running as a system daemon. It registers the node with the API server, watches for Pod assignments targeted at its host, and interacts directly with the local runtime to ensure containers match their declared &lt;code&gt;PodSpec&lt;/code&gt; definitions and remain healthy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Runtime:&lt;/strong&gt; The underlying software (such as containerd or CRI-O) responsible for pulling container images, configuring namespaces and cgroups, and running the container processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kube-proxy&lt;/code&gt;:&lt;/strong&gt; A network proxy running on each node that synchronizes host network rules (such as &lt;code&gt;iptables&lt;/code&gt; or IPVS). It translates abstract virtual IPs into direct routing pathways across dynamic worker workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Placement&lt;/th&gt;
&lt;th&gt;Statefulness&lt;/th&gt;
&lt;th&gt;Core Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;kube-apiserver&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;td&gt;Exposes the Kubernetes API and validates cluster read/write operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;etcd&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;Stateful&lt;/td&gt;
&lt;td&gt;Serves as the distributed, consistent key-value backing store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;kube-scheduler&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;td&gt;Selects target nodes for unassigned Pods using filtering and scoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;kube-controller-manager&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;td&gt;Executes core reconciliation loops to maintain desired cluster state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;kubelet&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Worker Node&lt;/td&gt;
&lt;td&gt;Stateless agent&lt;/td&gt;
&lt;td&gt;Communicates with the API server and enforces container specifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;kube-proxy&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Worker Node&lt;/td&gt;
&lt;td&gt;Stateless agent&lt;/td&gt;
&lt;td&gt;Manages local node network rules to direct traffic to target Pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container Runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Worker Node&lt;/td&gt;
&lt;td&gt;Local runtime&lt;/td&gt;
&lt;td&gt;Executes container lifecycles on the node OS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  3. The Workload Hierarchy: Pods, ReplicaSets, and Deployments
&lt;/h2&gt;

&lt;p&gt;To run applications reliably across a cluster, Kubernetes does not manage individual containers directly. Instead, it layers primitives into an abstraction hierarchy: &lt;strong&gt;Deployments&lt;/strong&gt; manage &lt;strong&gt;ReplicaSets&lt;/strong&gt;, which manage &lt;strong&gt;Pods&lt;/strong&gt;, which in turn house your application containers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fdemmqb6o%2Fblogs%2F13e26d4d1d4a3eb21ec2563f413d7a3ed2a00386-1284x1608.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fdemmqb6o%2Fblogs%2F13e26d4d1d4a3eb21ec2563f413d7a3ed2a00386-1284x1608.png" title="Kubernetes Workload Hierarchy: Deployment to Pods" alt="Kubernetes Workload Hierarchy: Deployment to Pods" width="800" height="1002"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Pod: The Atomic Unit of Scheduling
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Pod&lt;/strong&gt; is the smallest deployable atomic unit in Kubernetes. While a Docker workflow treats a single container as the deployment artifact, Kubernetes wraps one or more tightly coupled containers into a Pod.&lt;/p&gt;

&lt;p&gt;All containers inside the same Pod share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single &lt;strong&gt;network namespace&lt;/strong&gt; and loopback interface (&lt;code&gt;localhost&lt;/code&gt;), allowing intra-Pod processes to communicate via local ports.&lt;/li&gt;
&lt;li&gt;A shared &lt;strong&gt;Pod IP address&lt;/strong&gt; routable within the cluster network.&lt;/li&gt;
&lt;li&gt;Shared &lt;strong&gt;storage volumes&lt;/strong&gt; mounted into multiple containers simultaneously.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crucially, raw Pods are &lt;strong&gt;ephemeral&lt;/strong&gt; and non-durable. &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;Worker nodes&lt;/a&gt; host Pods, and the local &lt;a href="https://kubernetes.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;kubelet agent&lt;/a&gt; ensures containers defined in PodSpecs are running and healthy. However, if an entire host node fails, a standalone Pod is not rescheduled or migrated—it is permanently lost. Production workloads therefore require higher-level managers.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ReplicaSets: Declarative Self-Healing and Scale
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;ReplicaSet&lt;/strong&gt; guarantees that a specified number of identical Pod instances remain running at all times. It enforces this contract using an infinite &lt;a href="https://k8s.info/docs/foundations/architecture" rel="noopener noreferrer"&gt;reconciliation loop&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    Obs["1. Observe&amp;lt;br/&amp;gt;Query API server for&amp;lt;br/&amp;gt;matching Pod labels"]
    Comp["2. Compare&amp;lt;br/&amp;gt;Match active count&amp;lt;br/&amp;gt;against spec.replicas"]
    Act["3. Act&amp;lt;br/&amp;gt;Create missing Pods or&amp;lt;br/&amp;gt;prune excess Pods"]

    Obs --&amp;gt; Comp
    Comp --&amp;gt; Act
    Act --&amp;gt; Obs

    style Obs fill:#0369a1,stroke:#38bdf8,stroke-width:2px,color:#ffffff;
    style Comp fill:#b45309,stroke:#fbbf24,stroke-width:2px,color:#ffffff;
    style Act fill:#15803d,stroke:#4ade80,stroke-width:2px,color:#ffffff;&lt;/code&gt;&lt;/pre&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Observe:&lt;/strong&gt; Query the cluster state to count running Pods matching a specific label selector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare:&lt;/strong&gt; Evaluate the actual active Pod count against the desired target (&lt;code&gt;spec.replicas&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act:&lt;/strong&gt; Issue requests to the API server to create new Pods when counts drop below target, or terminate excess Pods if counts exceed target.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If an underlying worker node crashes, the &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;control plane&lt;/a&gt; notices the lost replicas during its reconciliation cycle and schedules replacement Pods onto healthy &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;worker machines&lt;/a&gt;, providing automated self-healing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Deployments: Declarative Rolling Updates and Rollbacks
&lt;/h3&gt;

&lt;p&gt;While ReplicaSets manage instance counts, they cannot smoothly upgrade running workloads. If you update a container image inside a standalone ReplicaSet, existing Pods continue running untouched until manually deleted.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Deployment&lt;/strong&gt; solves this by managing multiple ReplicaSets declaratively. When you apply a new container image or configuration, the Deployment controller provisions a new ReplicaSet, scales it up incrementally, and simultaneously scales down the old ReplicaSet. This mechanism provides zero-downtime rolling updates and instant rollbacks.&lt;/p&gt;

&lt;p&gt;Here is a complete, minimal Deployment specification defined in JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"apiVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"apps/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deployment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"labels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web-api"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"spec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"replicas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matchLabels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web-api"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"labels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web-api"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"spec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"containers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nginx:1.27-alpine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"ports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"containerPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You submit this declaration with &lt;code&gt;kubectl apply -f deployment.json&lt;/code&gt;. In this hierarchy, you define the desired operational state on the Deployment. The Deployment orchestrates the ReplicaSets, the ReplicaSets ensure the target quantity of Pods exist, and worker node runtimes execute your containers.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Service Networking: Stable Virtual IPs and Traffic Distribution
&lt;/h2&gt;

&lt;p&gt;While Deployments solve replication and rolling updates, they introduce a fundamental networking problem: &lt;strong&gt;Pod IPs are ephemeral&lt;/strong&gt;. Whenever a node dies, a deployment rolls out a new image, or a horizontal autoscaler shrinks your workload, the affected Pods are destroyed along with their dynamic IP addresses. If upstream callers or other internal microservices attempt to hardcode direct Pod IP connections, communication breaks the moment a Pod restarts.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Service Primitive and EndpointSlices
&lt;/h3&gt;

&lt;p&gt;To decouple client requests from ephemeral Pod IPs, Kubernetes introduces the &lt;strong&gt;Service&lt;/strong&gt; resource. A Service acts as a durable abstraction layer, assigning a permanent virtual IP (&lt;strong&gt;ClusterIP&lt;/strong&gt;) and a persistent cluster-internal DNS name to an arbitrary collection of Pods selected by labels.&lt;/p&gt;

&lt;p&gt;Rather than routing directly to pods, the Service points to dynamic &lt;strong&gt;EndpointSlices&lt;/strong&gt; that track the real-time IP addresses of ready, healthy Pods. As detailed in the &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/probes" rel="noopener noreferrer"&gt;Kubernetes documentation on probes&lt;/a&gt;, when a container fails its readiness probe, the control plane immediately strips the Pod IP from the corresponding EndpointSlice. The container stays running, but incoming traffic ceases until the probe passes again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fdemmqb6o%2Fblogs%2Fff2a591ab70413b5de8b42135f95278142f113a2-2984x764.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fdemmqb6o%2Fblogs%2Fff2a591ab70413b5de8b42135f95278142f113a2-2984x764.png" title="Kubernetes Service VIP &amp;amp; Traffic Distribution via EndpointSlices" alt="Kubernetes Service VIP &amp;amp; Traffic Distribution via EndpointSlices" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How kube-proxy Routes Traffic
&lt;/h3&gt;

&lt;p&gt;The virtual IP assigned to a Service is not bound to any physical network interface. Instead, &lt;a href="https://kubernetes.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;kube-proxy maintains network rules&lt;/a&gt; on every worker node to implement the Service abstraction. Running on worker nodes alongside the kubelet as described in &lt;a href="https://k8s.info/docs/foundations/architecture" rel="noopener noreferrer"&gt;The Kubernetes Visual Handbook&lt;/a&gt;, kube-proxy configures local packet-filtering mechanisms—such as Linux iptables or IPVS—to intercept requests directed to the Service's virtual IP and rewrite the destination to one of the healthy backend Pod IPs.&lt;/p&gt;

&lt;p&gt;Here is a minimal Service manifest in JSON routing port 80 traffic to Pods carrying the label &lt;code&gt;app: web-api&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"apiVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web-api"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"spec"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ClusterIP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"selector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"web-api"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"targetPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service Types and Exposure Scope
&lt;/h3&gt;

&lt;p&gt;Kubernetes defines multiple Service types depending on whether your workload is cluster-internal or needs to accept ingress from the outside world:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service Type&lt;/th&gt;
&lt;th&gt;Exposure Scope&lt;/th&gt;
&lt;th&gt;Routing Mechanism&lt;/th&gt;
&lt;th&gt;Primary Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Internal cluster only&lt;/td&gt;
&lt;td&gt;Default static virtual IP reachable strictly inside worker nodes.&lt;/td&gt;
&lt;td&gt;Inter-service communication, backend APIs, internal databases.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NodePort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External to cluster&lt;/td&gt;
&lt;td&gt;Allocates a dedicated port (usually 30000–32767) across every worker node's external IP.&lt;/td&gt;
&lt;td&gt;Development testing or direct node ingress without cloud load balancers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External to cluster&lt;/td&gt;
&lt;td&gt;Provisions a cloud provider load balancer (e.g., AWS NLB, GCP Load Balancer) that forwards traffic to NodePorts.&lt;/td&gt;
&lt;td&gt;Production-grade public traffic ingress for internet-facing applications.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5. Health Probes: Startup, Liveness, and Readiness
&lt;/h2&gt;

&lt;p&gt;In standalone Docker, the container runtime considers a container healthy as long as PID 1 remains active. However, real-world services can deadlock, exhaust thread pools, or remain unresponsive during lengthy startup routines while keeping their main process alive. To automate remediation and prevent traffic black-holing, the kubelet executes three dedicated health probes: &lt;strong&gt;Startup&lt;/strong&gt;, &lt;strong&gt;Liveness&lt;/strong&gt;, and &lt;strong&gt;Readiness&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Three Probe Types
&lt;/h3&gt;

&lt;p&gt;Each probe serves a distinct operational objective across the container lifecycle:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Probe Type&lt;/th&gt;
&lt;th&gt;Evaluation Timing&lt;/th&gt;
&lt;th&gt;Kubelet Action on Failure&lt;/th&gt;
&lt;th&gt;Operational Objective&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Startup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;At container launch until initial success&lt;/td&gt;
&lt;td&gt;Kills and restarts the container&lt;/td&gt;
&lt;td&gt;Shields slow-initializing applications from premature termination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Liveness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Post-startup, throughout container life&lt;/td&gt;
&lt;td&gt;Kills and restarts the container&lt;/td&gt;
&lt;td&gt;Recovers from deadlocks, infinite loops, and unrecoverable broken states&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Readiness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Post-startup, throughout container life&lt;/td&gt;
&lt;td&gt;Removes container IP from Service &lt;code&gt;EndpointSlices&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Isolates instances during heavy load, cache hydration, or temporary faults&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  1. Startup Probes: Guarding Slow Initialization
&lt;/h3&gt;

&lt;p&gt;When applications require extended time to hydrate in-memory caches, parse schemas, or execute database migrations, standard health checks can trigger premature restart loops. As detailed in the &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/probes" rel="noopener noreferrer"&gt;Kubernetes documentation on probes&lt;/a&gt;, a &lt;strong&gt;startup probe&lt;/strong&gt; disables both liveness and readiness checks until the container successfully completes initialization. If the startup probe does not succeed within its configured failure threshold, the kubelet terminates the container and enforces the Pod's restart policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Liveness Probes: Self-Healing Deadlocks
&lt;/h3&gt;

&lt;p&gt;Once an application marks itself as started, the kubelet runs &lt;strong&gt;liveness probes&lt;/strong&gt; to confirm ongoing progress. If an unhandled exception locks a worker thread or a resource leak causes an internal deadlock, the application fails this check. When consecutive failures exceed the configured threshold, the kubelet kills the container and initiates a restart to restore service availability.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Readiness Probes: Dynamic Traffic Gating
&lt;/h3&gt;

&lt;p&gt;A running process is not necessarily capable of processing live requests. An application might temporarily saturate its thread pool or face downstream throttling. According to the &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/probes" rel="noopener noreferrer"&gt;Kubernetes probe specification&lt;/a&gt;, failing a &lt;strong&gt;readiness probe&lt;/strong&gt; prompts the EndpointSlice controller to remove the Pod IP from the Service endpoints without terminating the process. Once the Pod recovers and passes the probe, traffic routing automatically resumes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Probe Configuration and the Cascading Restart Trap
&lt;/h3&gt;

&lt;p&gt;We configure health checks inside the container specification of a workload manifest using standard HTTP, TCP socket, or exec command probes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"api-server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nginx:1.25"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"containerPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"startupProbe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"httpGet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/healthz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"failureThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"periodSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"livenessProbe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"httpGet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/healthz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"initialDelaySeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"periodSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"readinessProbe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"httpGet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/ready"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"periodSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As described in the &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes" rel="noopener noreferrer"&gt;official probe configuration guide&lt;/a&gt;, &lt;code&gt;initialDelaySeconds&lt;/code&gt; determines the grace period before the initial check runs, and &lt;code&gt;periodSeconds&lt;/code&gt; defines the evaluation interval.&lt;/p&gt;

&lt;p&gt;A dangerous architectural anti-pattern is testing external dependencies (such as a database or downstream microservice) inside a &lt;strong&gt;liveness probe&lt;/strong&gt;. If that shared dependency encounters transient latency or downtime, every Pod's liveness check fails simultaneously. The kubelet will restart every replica across the cluster at once, turning a temporary dependency hiccup into a catastrophic, self-inflicted cascading outage.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Auxiliary Primitives and the End-to-End Request Lifecycle
&lt;/h2&gt;

&lt;p&gt;Beyond Pods, Deployments, and Services, a production-grade Kubernetes cluster relies on auxiliary primitives to manage security boundaries, runtime configuration, durable storage, and resource contention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supporting Primitives: Configuration, Isolation, and Quotas
&lt;/h3&gt;

&lt;p&gt;Modern applications decouple immutable code from environment-specific configuration and storage. Kubernetes handles these concerns with specialized API resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namespaces:&lt;/strong&gt; Provide virtual isolation within the same physical cluster, allowing multiple teams or deployment stages to coexist without naming collisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ConfigMaps and Secrets:&lt;/strong&gt; Inject plain-text configuration and sensitive credentials (such as tokens or API keys) into containers via environment variables or mounted volumes, decoupling runtime state from container images. According to the &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;Google Cloud GKE architecture guide&lt;/a&gt;, Kubernetes persists the state of objects like ConfigMaps, Secrets, and Deployments in the &lt;code&gt;etcd&lt;/code&gt; key-value store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PersistentVolumes (PV) and PVCs:&lt;/strong&gt; Abstract underlying cloud or physical block storage away from application Pods, ensuring data persists across container restarts and rescheduling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Requests and Limits:&lt;/strong&gt; Enforce compute governance. &lt;strong&gt;Requests&lt;/strong&gt; specify the baseline CPU and memory guaranteed for scheduling onto a node, while &lt;strong&gt;Limits&lt;/strong&gt; set strict ceilings preventing rogue workloads from exhausting node memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Auxiliary Primitive&lt;/th&gt;
&lt;th&gt;Primary Function&lt;/th&gt;
&lt;th&gt;Failure / Misconfiguration Risk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Namespace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-tenancy and resource boundary isolation&lt;/td&gt;
&lt;td&gt;Unintended cross-team resource clashes or quota exhaustion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ConfigMap / Secret&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decoupling environmental configuration from container images&lt;/td&gt;
&lt;td&gt;Application crashes if keys are missing; unencrypted secrets exposure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PV / PVC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Durable stateful storage decoupled from Pod lifecycles&lt;/td&gt;
&lt;td&gt;Pod scheduling blocks (&lt;code&gt;Pending&lt;/code&gt;) if physical storage cannot bind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Requests / Limits&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Capacity allocation and runaway memory containment&lt;/td&gt;
&lt;td&gt;Overcommitment causes node instability or OOM-killed containers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Ingress and the End-to-End Request Path
&lt;/h3&gt;

&lt;p&gt;While a &lt;code&gt;ClusterIP&lt;/code&gt; Service provides an internal virtual IP and a &lt;code&gt;NodePort&lt;/code&gt; exposes raw ports, managing hundreds of individual ports becomes unmaintainable. An &lt;strong&gt;Ingress&lt;/strong&gt; acts as a centralized Layer 7 reverse proxy, consolidating HTTP and HTTPS routing, path-based dispatching, and TLS termination behind a single public entry point.&lt;/p&gt;

&lt;p&gt;As detailed in &lt;a href="https://devopscube.com/kubernetes-architecture-explained" rel="noopener noreferrer"&gt;DevopsCube's Kubernetes Architecture analysis&lt;/a&gt;, a Kubernetes cluster divides responsibilities: control plane nodes maintain the cluster state while worker nodes execute container workloads. The &lt;a href="https://kubernetes.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;official Kubernetes architecture documentation&lt;/a&gt; explains that &lt;code&gt;kube-proxy&lt;/code&gt; runs on nodes to maintain network rules and implement Service virtual routing, while the &lt;code&gt;kube-apiserver&lt;/code&gt; acts as the control plane front end.&lt;/p&gt;

&lt;p&gt;Here is how an incoming user request traverses the cluster from edge to execution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Edge Entry:&lt;/strong&gt; An external client issues a request resolved by DNS to an external Load Balancer routing into the cluster's Ingress Controller.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path Matching:&lt;/strong&gt; The Ingress Controller inspects the HTTP host header and URI path, mapping the request to the target cluster &lt;code&gt;Service&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Translation:&lt;/strong&gt; The packet reaches a worker node where &lt;a href="https://kubernetes.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;kube-proxy network rules&lt;/a&gt; translate the Service's virtual IP into a healthy, ready Pod IP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution:&lt;/strong&gt; The target Pod receives the request, processes it inside the container runtime, and writes persistent updates to its mounted PVC.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgpy5alzn56lh1pj7qv0r.png" alt="K8s Request Lifecycle with Auxiliary Primitives" width="799" height="409"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  7. Summary &amp;amp; What's Next
&lt;/h2&gt;

&lt;p&gt;We have transitioned from viewing containers as standalone, isolated runtimes to understanding how Kubernetes coordinates them into a cohesive, self-healing system. At its foundation, a Kubernetes cluster divides responsibility between an administrative &lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;control plane and worker nodes&lt;/a&gt; that execute your workloads. Instead of managing container lifecycles manually, you declare your desired system state in manifests, and Kubernetes relies on &lt;a href="https://k8s.info/docs/foundations/architecture" rel="noopener noreferrer"&gt;continuous reconciliation loops&lt;/a&gt; to observe actual state and correct discrepancies automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Primitives Summary
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Role in Cluster&lt;/th&gt;
&lt;th&gt;Operational Takeaway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Control Plane&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manages cluster state, scheduling, and API requests&lt;/td&gt;
&lt;td&gt;Central authority running the API server, scheduler, and controller managers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worker Node&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs containerized workloads&lt;/td&gt;
&lt;td&gt;Hosts the &lt;code&gt;kubelet&lt;/code&gt;, container runtime, and &lt;code&gt;kube-proxy&lt;/code&gt; proxying traffic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pod&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Smallest deployable compute unit&lt;/td&gt;
&lt;td&gt;Colocates tightly coupled containers sharing network and storage namespaces; ephemeral by default.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manages ReplicaSets declaratively&lt;/td&gt;
&lt;td&gt;Provides automated rollouts, rollbacks, and replica scaling without manual intervention.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stable network abstraction&lt;/td&gt;
&lt;td&gt;Gives ephemeral Pods a persistent virtual IP and distributes traffic across healthy instances.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Health Probes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Startup, liveness, and readiness checks&lt;/td&gt;
&lt;td&gt;Prevents traffic from reaching unready containers and restarts stalled processes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What's Next: Hands-On Practice
&lt;/h3&gt;

&lt;p&gt;To move from architecture to implementation, test these primitives in a lightweight local cluster:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Spin up a local cluster:&lt;/strong&gt; Use local orchestrators like Minikube or Kind (&lt;code&gt;kind create cluster&lt;/code&gt;) to run a multi-node cluster environment directly on your workstation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy your first workload:&lt;/strong&gt; Write a basic &lt;code&gt;Deployment&lt;/code&gt; YAML exposing an NGINX container, apply it with &lt;code&gt;kubectl apply -f deployment.yaml&lt;/code&gt;, and scale replicas using &lt;code&gt;kubectl scale&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expose and test networking:&lt;/strong&gt; Attach a &lt;code&gt;ClusterIP&lt;/code&gt; or &lt;code&gt;NodePort&lt;/code&gt; Service to verify internal DNS resolution and traffic distribution across Pod IPs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect and debug:&lt;/strong&gt; Practice cluster troubleshooting using key diagnostic commands:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inspect workload health and scheduled events&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide
kubectl describe pod &amp;lt;pod-name&amp;gt;

&lt;span class="c"&gt;# Stream logs and open an interactive shell inside a running container&lt;/span&gt;
kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;pod-name&amp;gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--&lt;/span&gt; /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/concepts/architecture" rel="noopener noreferrer"&gt;Cluster Architecture&lt;/a&gt; — Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://k8s.info/docs/foundations/architecture" rel="noopener noreferrer"&gt;Architecture | The Kubernetes Visual Handbook&lt;/a&gt; — k8s.info&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture" rel="noopener noreferrer"&gt;GKE cluster architecture | Google Kubernetes Engine (GKE) | Google Cloud Documentation&lt;/a&gt; — Google Cloud Documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/concepts/overview/components" rel="noopener noreferrer"&gt;Kubernetes Components&lt;/a&gt; — Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devopscube.com/kubernetes-architecture-explained" rel="noopener noreferrer"&gt;Kubernetes Architecture Explained (With Illustrated Diagrams)&lt;/a&gt; — DevOpsCube – Easy DevOps, SRE Guides &amp;amp; Reviews&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/probes" rel="noopener noreferrer"&gt;Liveness, Readiness, and Startup Probes&lt;/a&gt; — Kubernetes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes" rel="noopener noreferrer"&gt;Configure Liveness, Readiness and Startup Probes&lt;/a&gt; — Kubernetes&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://leon-pham.vercel.app/posts/kubernetes-architecture-and-primitives" rel="noopener noreferrer"&gt;https://leon-pham.vercel.app/posts/kubernetes-architecture-and-primitives&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>k8s</category>
      <category>devops</category>
      <category>docker</category>
    </item>
    <item>
      <title>Step-by-Step Guide: Fine-Tuning an IELTS Examiner LLM with Unsloth, QLoRA</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Tue, 20 Oct 2026 16:02:54 +0000</pubDate>
      <link>https://dev.to/longtk26/step-by-step-guide-fine-tuning-an-ielts-examiner-llm-with-unsloth-qlora-20a1</link>
      <guid>https://dev.to/longtk26/step-by-step-guide-fine-tuning-an-ielts-examiner-llm-with-unsloth-qlora-20a1</guid>
      <description>&lt;p&gt;In &lt;strong&gt;&lt;a href="https://dev.to/longtk26/llm-fine-tuning-concepts-architecture-and-fine-tuning-vs-rag-1j84"&gt;Part 1: Concepts &amp;amp; Architecture&lt;/a&gt;&lt;/strong&gt;, we explored what fine-tuning is, how it differs fundamentally from RAG, and how QLoRA enables training large models on consumer hardware.&lt;/p&gt;

&lt;p&gt;In this practical, hands-on tutorial, we will build a domain-specialized LLM from scratch: &lt;strong&gt;an official IELTS Examiner AI&lt;/strong&gt;. We will fine-tune an 8-Billion parameter foundation model (&lt;strong&gt;Qwen 2.5 7B&lt;/strong&gt;) directly on a free Google Colab instance, export the model to GGUF format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The Tool Stack: Why Unsloth + QLoRA?&lt;/li&gt;
&lt;li&gt;Step 1: Dataset Engineering for IELTS Evaluation&lt;/li&gt;
&lt;li&gt;Step 2: Colab Environment Setup &amp;amp; GPU Check&lt;/li&gt;
&lt;li&gt;Step 3: Loading the 4-bit Base Model&lt;/li&gt;
&lt;li&gt;Step 4: Attaching LoRA Adapters&lt;/li&gt;
&lt;li&gt;Step 5: Formatting Prompts &amp;amp; Chat Templates&lt;/li&gt;
&lt;li&gt;Step 6: Configuring SFTTrainer &amp;amp; Training&lt;/li&gt;
&lt;li&gt;Step 7: Qualitative Inference &amp;amp; Testing&lt;/li&gt;
&lt;li&gt;Step 8: Exporting to GGUF &amp;amp; Serving with Ollama&lt;/li&gt;
&lt;li&gt;Troubleshooting &amp;amp; Pro Tips&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. The Tool Stack: Why Unsloth + QLoRA?
&lt;/h2&gt;

&lt;p&gt;Fine-tuning an 8B parameter model natively requires over &lt;strong&gt;110 GB of VRAM&lt;/strong&gt;. On Google Colab's free tier, we are constrained to a single &lt;strong&gt;NVIDIA Tesla T4 GPU (15.3 GB VRAM)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To make this feasible, we use:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Why We Selected It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://github.com/unslothai/unsloth" rel="noopener noreferrer"&gt;Unsloth&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fine-tuning Acceleration&lt;/td&gt;
&lt;td&gt;Cuts VRAM consumption by &lt;strong&gt;70%&lt;/strong&gt; and speeds up training by &lt;strong&gt;2x&lt;/strong&gt; without any loss of accuracy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BitsAndBytes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4-bit NormalFloat (NF4)&lt;/td&gt;
&lt;td&gt;Compresses the 16-bit base model (~16 GB) down to just &lt;strong&gt;~5.5 GB&lt;/strong&gt;, fitting comfortably on the T4 GPU.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PEFT (Hugging Face)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Parameter-Efficient Fine-Tuning&lt;/td&gt;
&lt;td&gt;Trains only low-rank adapter matrices ($r=16$), representing just &lt;strong&gt;0.53%&lt;/strong&gt; of all model parameters (40.3M trainable parameters out of 7.6B).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TRL (&lt;code&gt;SFTTrainer&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supervised Training Loop&lt;/td&gt;
&lt;td&gt;Handles batching, sequence packing, loss masking, and gradient accumulation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;llama.cpp + Ollama&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quantization &amp;amp; Local Serving&lt;/td&gt;
&lt;td&gt;Compiles the fine-tuned model into a single 4.5 GB &lt;code&gt;.gguf&lt;/code&gt; file that runs with low latency on your local laptop (CPU, Mac M-series, or NVIDIA GPU).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 1: Dataset Engineering for IELTS Evaluation
&lt;/h2&gt;

&lt;p&gt;The model's grading accuracy and feedback quality depend entirely on the consistency of the training data. For IELTS, our model must evaluate essays strictly according to the &lt;strong&gt;four official Cambridge assessment criteria&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Task Response (TR)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coherence &amp;amp; Cohesion (CC)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lexical Resource (LR)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grammatical Range &amp;amp; Accuracy (GRA)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ChatML Data Schema (&lt;code&gt;sample_ielts_dataset.jsonl&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Each training row is a JSON object with a &lt;code&gt;"messages"&lt;/code&gt; array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You are an official IELTS Senior Examiner. Evaluate the candidate's response using the official IELTS Band Descriptors: Task Response (TR), Coherence and Cohesion (CC), Lexical Resource (LR), and Grammatical Range and Accuracy (GRA). Provide overall and component band scores, diagnostic feedback, specific sentence-level corrections, and a Band 8.5+ model revision."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IELTS Writing Task 2 Prompt:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;In many cities, the use of private cars is increasing rapidly. What are the problems associated with this, and what measures can be taken to solve them?&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Candidate Essay:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;In modern cities, people buy more cars than before. This phenomenon cause many issues for society and environment..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"### 📊 Estimated Band Score: 6.0&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;#### Component Scores Breakdown:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- **Task Response (6.5):** Clearly identifies problems (traffic congestion, air pollution) and plausible solutions...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- **Coherence &amp;amp; Cohesion (6.0):** Information is logically arranged, but transitions are mechanical...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- **Lexical Resource (5.5):** Adequate for basic communication, but relies on informal phrasing...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- **Grammatical Range &amp;amp; Accuracy (5.5):** Frequent errors with articles and subject-verb agreements...&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;#### ✍️ Diagnostic Sentence Improvements:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;1. *Original:* 'This phenomenon cause many issues...'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;   *Correction:* 'This phenomenon causes numerous detrimental consequences for both society and the environment.'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;#### 🌟 Band 8.5+ Model Rephrase:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;'The proliferation of private motor vehicles precipitates severe urban complications...'"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Colab Environment Setup &amp;amp; GPU Check
&lt;/h2&gt;

&lt;p&gt;Open Google Colab and set your runtime to GPU: &lt;code&gt;Runtime &amp;gt; Change runtime type &amp;gt; T4 GPU&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Pre-built Wheels (Avoiding Source Compilation)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Check GPU hardware
&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;nvidia&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;smi&lt;/span&gt;

&lt;span class="c1"&gt;# Fast installation using pre-compiled binary wheels (~1 minute)
&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="n"&gt;trl&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="n"&gt;accelerate&lt;/span&gt; &lt;span class="n"&gt;bitsandbytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Critical Gotcha:&lt;/strong&gt; Never run &lt;code&gt;!pip install "xformers&amp;lt;0.0.27"&lt;/code&gt; on modern Colab. Doing so triggers a 40-minute source compilation (&lt;code&gt;Building wheels for collected packages: xformers&lt;/code&gt;). Installing via the command above installs pre-built binary wheels in under 90 seconds.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Loading the 4-bit Base Model
&lt;/h2&gt;

&lt;p&gt;We load &lt;strong&gt;&lt;code&gt;Qwen2.5-7B-Instruct&lt;/code&gt;&lt;/strong&gt; in 4-bit precision via Unsloth's &lt;code&gt;FastLanguageModel&lt;/code&gt;. Qwen 2.5 is exceptional at linguistic reasoning, grammatical parsing, and instruction following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unsloth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastLanguageModel&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="n"&gt;max_seq_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;  &lt;span class="c1"&gt;# Ample context for full essay + comprehensive grading feedback
&lt;/span&gt;&lt;span class="n"&gt;dtype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;           &lt;span class="c1"&gt;# Auto-detection (Float16 on T4)
&lt;/span&gt;&lt;span class="n"&gt;load_in_4bit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;    &lt;span class="c1"&gt;# 4-bit NormalFloat (NF4)
&lt;/span&gt;
&lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsloth/Qwen2.5-7B-Instruct-bnb-4bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastLanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_in_4bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;load_in_4bit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Memory Footprint:&lt;/strong&gt; The entire 7.6B model is loaded into only &lt;strong&gt;~5.5 GB of VRAM&lt;/strong&gt;, leaving ~9 GB of free VRAM for activations and optimizer states.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Attaching LoRA Adapters
&lt;/h2&gt;

&lt;p&gt;Next, we wrap the base model with low-rank trainable matrices targeting both the attention projections and the feed-forward MLP layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FastLanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_peft_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;# LoRA rank (16-32 provides optimal expressive capacity)
&lt;/span&gt;    &lt;span class="n"&gt;target_modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;up_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;down_proj&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;lora_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;# Scaling factor (usually equals r or 2*r)
&lt;/span&gt;    &lt;span class="n"&gt;lora_dropout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# Optimized to 0 by Unsloth for maximum speed
&lt;/span&gt;    &lt;span class="n"&gt;bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;use_gradient_checkpointing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsloth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Drastically cuts activation memory
&lt;/span&gt;    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3407&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Formatting Prompts &amp;amp; Chat Templates
&lt;/h2&gt;

&lt;p&gt;We apply the model's native chat template to convert the multi-role JSON conversation into standard token sequences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dataset&lt;/span&gt;

&lt;span class="c1"&gt;# Load your JSONL dataset
# dataset = load_dataset("json", data_files="sample_ielts_dataset.jsonl")
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formatting_prompts_func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;convos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;convo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add_generation_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;convo&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;convos&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formatting_prompts_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batched&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Configuring SFTTrainer &amp;amp; Training
&lt;/h2&gt;

&lt;p&gt;Now we configure the &lt;code&gt;SFTTrainer&lt;/code&gt; with an 8-bit AdamW optimizer and cosine learning rate schedule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;trl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SFTTrainer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TrainingArguments&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unsloth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;is_bfloat16_supported&lt;/span&gt;

&lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SFTTrainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;train_dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dataset_text_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_seq_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dataset_num_proc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;packing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;TrainingArguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Effective batch size = 2 * 4 = 8
&lt;/span&gt;        &lt;span class="n"&gt;warmup_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;# Adjust based on dataset size (e.g. 100-300)
&lt;/span&gt;        &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2e-4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;# Standard LoRA learning rate
&lt;/span&gt;        &lt;span class="n"&gt;fp16&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;is_bfloat16_supported&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;bf16&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;is_bfloat16_supported&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;logging_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;optim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adamw_8bit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;# Saves 75% optimizer memory
&lt;/span&gt;        &lt;span class="n"&gt;weight_decay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;lr_scheduler_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cosine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3407&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;output_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ielts_outputs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;trainer_stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;train&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Qualitative Inference &amp;amp; Testing
&lt;/h2&gt;

&lt;p&gt;We switch the model to inference mode (which Unsloth accelerates by an additional 2x) and test on a completely unseen essay:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;FastLanguageModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;for_inference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;test_messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are an official IELTS Senior Examiner. Grade the candidate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s essay using the official IELTS Band Descriptors (Task Response, Coherence and Cohesion, Lexical Resource, Grammatical Range and Accuracy). Provide estimated band scores, granular feedback for each criterion, sentence-level corrections, and a Band 8.5+ model revision.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Prompt: Some people say that advertising encourages us to buy things we do not really need. Others think that advertisements inform us about new products that can improve our lives. Discuss both views and give your opinion.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Candidate Essay:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Advertising is everywhere today. In television and internet, we can see many ads. Some people think advertisements make people waste money on useless items, but others think ads provide useful knowledge. I agree that ads are helpful if we are smart consumers.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;test_messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;add_generation_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Low temperature ensures calibrated, deterministic grading
&lt;/span&gt;    &lt;span class="n"&gt;use_cache&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:],&lt;/span&gt; &lt;span class="n"&gt;skip_special_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8: Exporting to GGUF &amp;amp; Serving with Ollama
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Save Adapter &amp;amp; Export 4-bit GGUF in Colab
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Save LoRA weights locally
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ielts_lora_adapter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ielts_lora_adapter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Automatically merge weights into 16-bit and quantize to GGUF Q4_K_M
&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained_gguf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ielts_examiner_gguf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantization_method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q4_k_m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compiles &lt;code&gt;ielts_examiner_gguf_gguf/Qwen2.5-7B-Instruct.Q4_K_M.gguf&lt;/code&gt; (~4.5 GB) and an automatic &lt;code&gt;Modelfile&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Backup to Google Drive
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.colab&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;drive&lt;/span&gt;
&lt;span class="n"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/content/drive&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;cp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="n"&gt;ielts_examiner_gguf_gguf&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;drive&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;MyDrive&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Run Locally with Ollama
&lt;/h3&gt;

&lt;p&gt;Download the folder from Google Drive to your local machine, open your terminal, and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Register the model with Ollama&lt;/span&gt;
ollama create ielts-examiner &lt;span class="nt"&gt;-f&lt;/span&gt; Modelfile

&lt;span class="c"&gt;# Start an interactive session&lt;/span&gt;
ollama run ielts-examiner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have a private, dedicated IELTS Examiner AI running locally on your laptop with zero API fees!&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Building wheels for collected packages: xformers&lt;/code&gt; hangs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Incompatible pip version attempting to build CUDA C++ from source&lt;/td&gt;
&lt;td&gt;Avoid pinning &lt;code&gt;xformers&amp;lt;0.0.27&lt;/code&gt;; use the modern install command with pre-built wheels shown in Step 2.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CUDA Out-of-Memory (OOM)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sequence length too long or batch size too large&lt;/td&gt;
&lt;td&gt;Reduce &lt;code&gt;per_device_train_batch_size&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;2&lt;/code&gt;, increase &lt;code&gt;gradient_accumulation_steps&lt;/code&gt; to &lt;code&gt;8&lt;/code&gt;, and ensure &lt;code&gt;use_gradient_checkpointing="unsloth"&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grade Inflation (Scores too high)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dataset consists primarily of high-band essays&lt;/td&gt;
&lt;td&gt;Ensure at least 30% of your training dataset consists of Band 4.5–5.5 essays with rigorous feedback explaining their weaknesses.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lost Files after Colab Disconnect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Colab runtime disk is temporary&lt;/td&gt;
&lt;td&gt;Always mount Google Drive at the beginning of the notebook and save checkpoints directly to &lt;code&gt;/content/drive/MyDrive/&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion &amp;amp; Resources
&lt;/h2&gt;

&lt;p&gt;By combining &lt;strong&gt;QLoRA&lt;/strong&gt; with &lt;strong&gt;Unsloth&lt;/strong&gt;, we transformed an 8B foundation model into a specialized IELTS assessment engine within a single hour on free cloud hardware.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>finetuning</category>
      <category>llm</category>
    </item>
    <item>
      <title>LLM Fine-Tuning: Concepts, Architecture, and Fine-Tuning vs. RAG</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Tue, 20 Oct 2026 16:01:45 +0000</pubDate>
      <link>https://dev.to/longtk26/llm-fine-tuning-concepts-architecture-and-fine-tuning-vs-rag-1j84</link>
      <guid>https://dev.to/longtk26/llm-fine-tuning-concepts-architecture-and-fine-tuning-vs-rag-1j84</guid>
      <description>&lt;p&gt;Fine-tuning has emerged as one of the most critical skills for modern AI engineers. While large general-purpose foundation models (like GPT, Claude, or Qwen) are impressive out of the box, building production-grade domain applications often demands specialized behavior, deterministic formatting, or deep domain expertise that prompting alone cannot reliably achieve.&lt;/p&gt;

&lt;p&gt;In this guide, we break down the foundational concepts of LLM fine-tuning, compare it with Retrieval-Augmented Generation (RAG), examine parameter-efficient training techniques (LoRA/QLoRA), and help you decide exactly when and when not to fine-tune.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
What is Fine-Tuning?

&lt;ul&gt;
&lt;li&gt;The LLM Training Lifecycle&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Why Fine-Tune? Core Motivations&lt;/li&gt;
&lt;li&gt;
Fine-Tuning vs. RAG

&lt;ul&gt;
&lt;li&gt;Core Mental Model: Open-Book vs. Closed-Book Exam&lt;/li&gt;
&lt;li&gt;Detailed Comparison&lt;/li&gt;
&lt;li&gt;The Hybrid Approach: RAG + Fine-Tuning&lt;/li&gt;
&lt;li&gt;Decision Framework: Which Should You Choose?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
How an LLM Processes Input and Generates Output (Under the Hood)

&lt;ul&gt;
&lt;li&gt;The End-to-End Inference Lifecycle&lt;/li&gt;
&lt;li&gt;The Tokenization Mechanism: From Words to Numbers&lt;/li&gt;
&lt;li&gt;Internal Processing: Embeddings, Positional Encoding &amp;amp; Attention&lt;/li&gt;
&lt;li&gt;The Prediction Loop: Logits, Softmax &amp;amp; Autoregressive Decoding&lt;/li&gt;
&lt;li&gt;Sampling Controls: Temperature, Top-p &amp;amp; Stop Tokens&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Methods of Fine-Tuning

&lt;ul&gt;
&lt;li&gt;Full Parameter Fine-Tuning (FPFT)&lt;/li&gt;
&lt;li&gt;Parameter-Efficient Fine-Tuning (PEFT) &amp;amp; LoRA&lt;/li&gt;
&lt;li&gt;QLoRA: 4-bit Quantized LoRA&lt;/li&gt;
&lt;li&gt;Hardware Comparison: FPFT vs. LoRA vs. QLoRA&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Essential Hyperparameters &amp;amp; Micro-Concepts

&lt;ul&gt;
&lt;li&gt;Epochs vs. Steps vs. Batch Size&lt;/li&gt;
&lt;li&gt;Gradient Accumulation &amp;amp; Effective Batch Size&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
When is Fine-Tuning Useful? (And When is it Not?)

&lt;ul&gt;
&lt;li&gt;Best Scenarios for Fine-Tuning&lt;/li&gt;
&lt;li&gt;Scenarios Where You Should NOT Fine-Tune&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Summary &amp;amp; What's Next&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. What is Fine-Tuning?
&lt;/h2&gt;

&lt;p&gt;At its core, &lt;strong&gt;fine-tuning&lt;/strong&gt; is the process of taking a pre-trained model (which already understands grammar, world facts, and language patterns from trillions of tokens) and training it further on a curated, domain-specific dataset to adapt its weights for a specific task, persona, or style.&lt;/p&gt;

&lt;h3&gt;
  
  
  The LLM Training Lifecycle
&lt;/h3&gt;

&lt;p&gt;Modern language models undergo a three-stage lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    A["1. Pre-training&amp;lt;br/&amp;gt;Web Corpus&amp;lt;br/&amp;gt;Base Knowledge"] --&amp;gt; B["2. SFT / Instruct&amp;lt;br/&amp;gt;Curated QA&amp;lt;br/&amp;gt;Instruction Following"]
    B --&amp;gt; C["3. Alignment&amp;lt;br/&amp;gt;RLHF / DPO&amp;lt;br/&amp;gt;Safety &amp;amp; Preference"]

    style A fill:#0369a1,stroke:#38bdf8,stroke-width:2px,color:#ffffff;
    style B fill:#15803d,stroke:#4ade80,stroke-width:2px,color:#ffffff;
    style C fill:#b45309,stroke:#fbbf24,stroke-width:2px,color:#ffffff;&lt;/code&gt;&lt;/pre&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pre-training (Base Models):&lt;/strong&gt; Models like &lt;code&gt;Llama-3-8B-Base&lt;/code&gt; or &lt;code&gt;Qwen2.5-7B&lt;/code&gt; read massive swaths of the internet. Their objective is raw next-token prediction. They complete sentences, but they cannot converse or reliably follow instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supervised Fine-Tuning (SFT / Instruct Models):&lt;/strong&gt; The base model is trained on question-answer or multi-turn conversational datasets. This transforms a raw text completer into an assistant (&lt;code&gt;Llama-3-8B-Instruct&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain-Specific Fine-Tuning (Our Focus):&lt;/strong&gt; You take an Instruct model and fine-tune it on proprietary tasks—for example, evaluating IELTS essays according to strict Cambridge band descriptors.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. Why Fine-Tune? Core Motivations
&lt;/h2&gt;

&lt;p&gt;Why go through the effort of fine-tuning when we can simply write a prompt for GPT-4 or Claude?&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;mindmap
  root((Why Fine-Tune?))
    Style &amp;amp; Persona
      Strict tone consistency
      Unwavering examiner persona
      Specialized grading rubrics
    Output Determinism
      Reliable JSON schemas
      Predictable structured output
      Zero prompt leakage
    Efficiency &amp;amp; Cost
      Distill 70B behavior into 7B
      Drastically shorter prompts
      Lower latency and API costs
    Edge &amp;amp; Privacy
      Run on-premise or locally
      Zero data sent to third parties
      Full sovereignty over weights&lt;/code&gt;&lt;/pre&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Strict Style and Persona Consistency:&lt;/strong&gt; Prompt engineering often suffers from "drift" or breaks character over extended contexts. Fine-tuning hardcodes the persona directly into the model's weights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Output Formatting:&lt;/strong&gt; When your application requires a strict multi-section output, fine-tuned models produce the exact schema reliably without needing fragile retry loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Compression &amp;amp; Latency Reduction:&lt;/strong&gt; With zero-shot prompting, you might need to send 2,000 tokens of rubrics, instructions, and few-shot examples with &lt;em&gt;every single API request&lt;/em&gt;. Fine-tuning bakes those instructions into the model, cutting input prompt lengths by up to 80% and slashing latency and inference costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance on Edge Hardware:&lt;/strong&gt; You can fine-tune a compact 7B or 8B model to rival or outperform a 70B+ generic model on your specific vertical task, allowing you to deploy on lightweight hardware or on-premise servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Privacy &amp;amp; Governance:&lt;/strong&gt; Fine-tuning open-weight models allows enterprises to keep all sensitive customer data within their own Virtual Private Cloud (VPC) or local machine.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. Fine-Tuning vs. RAG
&lt;/h2&gt;

&lt;p&gt;One of the most frequent question in AI is: &lt;strong&gt;"Should I use RAG or Fine-Tuning?"&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Mental Model: Open-Book vs. Closed-Book Exam
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG (Retrieval-Augmented Generation) is an Open-Book Exam:&lt;/strong&gt; The student has good general reasoning skills. When asked a question, they look up external manuals, company wikis, or database records and synthesize an answer based on what they just retrieved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Tuning is Studying for the Exam (Internalized Expertise):&lt;/strong&gt; The student has memorized patterns, stylistic habits, and specialized procedural skills. When asked to perform, the reaction is instinctive and fast.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Detailed Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Retrieval-Augmented Generation (RAG)&lt;/th&gt;
&lt;th&gt;Supervised Fine-Tuning (SFT)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Injecting &lt;strong&gt;dynamic, up-to-date factual knowledge&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Modifying &lt;strong&gt;form, behavior, tone, style, and reasoning patterns&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Knowledge Freshness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Real-time:&lt;/strong&gt; Update your vector database, and the model instantly accesses new documents&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Static:&lt;/strong&gt; Knowledge is frozen at training time; updating requires retraining&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hallucination Mitigation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High: Grounded directly in retrieved source chunks with explicit citations&lt;/td&gt;
&lt;td&gt;Moderate: Can still hallucinate facts if trained on noisy or contradictory data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost Profile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low upfront setup; higher recurring per-query cost (longer context embeddings &amp;amp; tokens)&lt;/td&gt;
&lt;td&gt;Higher upfront compute cost; lower recurring query cost (shorter prompts, smaller models)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slower (Vector search + Embedding generation + Large context window)&lt;/td&gt;
&lt;td&gt;Faster (Direct inference with compact prompts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context Window Consumption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (Passages and reference chunks occupy prompt tokens)&lt;/td&gt;
&lt;td&gt;Low (Knowledge and rules are internal to the model)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Hybrid Approach: RAG + Fine-Tuning
&lt;/h3&gt;

&lt;p&gt;In modern production systems, RAG and Fine-Tuning are &lt;strong&gt;not mutually exclusive&lt;/strong&gt;—they are complementary:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    UserQuery["User Prompt&amp;lt;br/&amp;gt;Essay + Exam Question"] --&amp;gt; RAG["RAG Module&amp;lt;br/&amp;gt;Official Rubrics Database"]
    RAG --&amp;gt; ContextAugmented["Augmented Prompt&amp;lt;br/&amp;gt;User Essay + Rubrics"]
    ContextAugmented --&amp;gt; FTModel["Fine-Tuned 8B Model&amp;lt;br/&amp;gt;Calibrated Examiner Persona"]
    FTModel --&amp;gt; Output["Assessment Report&amp;lt;br/&amp;gt;Accurate &amp;amp; Structured"]

    style RAG fill:#0369a1,stroke:#38bdf8,stroke-width:2px,color:#ffffff;
    style FTModel fill:#15803d,stroke:#4ade80,stroke-width:2px,color:#ffffff;
    style Output fill:#7e22ce,stroke:#c084fc,stroke-width:2px,color:#ffffff;&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Fine-Tuning&lt;/strong&gt; to teach the model &lt;strong&gt;HOW&lt;/strong&gt; to behave (e.g. grade like an official Cambridge examiner, never give generic praise, generate exact JSON).&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;RAG&lt;/strong&gt; to feed the model &lt;strong&gt;WHAT&lt;/strong&gt; facts or reference materials to consult (e.g. the specific exam version question, updated vocabulary).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Decision Framework: Which Should You Choose?
&lt;/h3&gt;

&lt;p&gt;Use this decision tree to pick the right strategy:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    Start["Improve LLM Performance"] --&amp;gt; Q1{"Need dynamic or&amp;lt;br/&amp;gt;private facts?"}
    Q1 -- Yes --&amp;gt; Q2{"Changes daily&amp;lt;br/&amp;gt;or frequently?"}
    Q2 -- Yes --&amp;gt; UseRAG["✅ Use RAG&amp;lt;br/&amp;gt;(Vector DB + Search)"]
    Q2 -- No --&amp;gt; Q3{"Need custom tone&amp;lt;br/&amp;gt;or structure?"}
    Q3 -- Yes --&amp;gt; UseHybrid["✅ Use Hybrid&amp;lt;br/&amp;gt;(RAG + Fine-Tuning)"]
    Q3 -- No --&amp;gt; UseRAG

    Q1 -- No --&amp;gt; Q4{"Works with&amp;lt;br/&amp;gt;few-shot prompts?"}
    Q4 -- Yes --&amp;gt; UsePrompt["✅ Use Prompting&amp;lt;br/&amp;gt;(Fastest &amp;amp; Low Cost)"]
    Q4 -- No --&amp;gt; Q5{"Have 500+ quality&amp;lt;br/&amp;gt;examples?"}
    Q5 -- Yes --&amp;gt; UseFT["✅ Fine-Tune&amp;lt;br/&amp;gt;(PEFT / QLoRA)"]
    Q5 -- No --&amp;gt; CollectData["⚠️ Curate Data&amp;lt;br/&amp;gt;(Bootstrap Pairs First)"]

    style Start fill:#1e293b,stroke:#94a3b8,stroke-width:2px,color:#ffffff;
    style UseRAG fill:#0369a1,stroke:#38bdf8,stroke-width:2px,color:#ffffff;
    style UseHybrid fill:#7e22ce,stroke:#c084fc,stroke-width:2px,color:#ffffff;
    style UsePrompt fill:#15803d,stroke:#4ade80,stroke-width:2px,color:#ffffff;
    style UseFT fill:#c2410c,stroke:#fb923c,stroke-width:2px,color:#ffffff;
    style CollectData fill:#b45309,stroke:#fbbf24,stroke-width:2px,color:#ffffff;&lt;/code&gt;&lt;/pre&gt;






&lt;h2&gt;
  
  
  4. How an LLM Processes Input and Generates Output (Under the Hood)
&lt;/h2&gt;

&lt;p&gt;To understand how fine-tuning alters a model's behavior, we must first understand how an LLM natively receives text, processes it through its neural architecture, and generates a response.&lt;/p&gt;

&lt;h3&gt;
  
  
  The End-to-End Inference Lifecycle
&lt;/h3&gt;

&lt;p&gt;When you ask an LLM to evaluate an IELTS essay, the following cycle occurs:&lt;/p&gt;

&lt;h4&gt;
  
  
  Architectural Flow Breakdown:
&lt;/h4&gt;



&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    In["1. User Prompt&amp;lt;br/&amp;gt;(Raw String)"] --&amp;gt; Tok["2. Tokenizer&amp;lt;br/&amp;gt;(BPE / IDs)"]
    Tok --&amp;gt; Emb["3. Embedding&amp;lt;br/&amp;gt;+ RoPE"]
    Emb --&amp;gt; Trans["4. 28 Layers&amp;lt;br/&amp;gt;(Attention + LoRA)"]
    Trans --&amp;gt; Head["5. LM Head&amp;lt;br/&amp;gt;(Logits)"]
    Head --&amp;gt; Soft["6. Softmax&amp;lt;br/&amp;gt;(Temp T=0.3)"]
    Soft --&amp;gt; Sam["7. Next Token&amp;lt;br/&amp;gt;(Top-p=0.9)"]
    Sam --&amp;gt; Loop{"Stop Token&amp;lt;br/&amp;gt;&amp;amp;lt;|im_end|&amp;amp;gt;?"}
    Loop -- No --&amp;gt; Trans
    Loop -- Yes --&amp;gt; Out["8. Output Essay&amp;lt;br/&amp;gt;Review Report"]

    style In fill:#0f172a,stroke:#38bdf8,stroke-width:2px,color:#f8fafc;
    style Tok fill:#1e293b,stroke:#818cf8,stroke-width:2px,color:#f8fafc;
    style Emb fill:#1e293b,stroke:#a855f7,stroke-width:2px,color:#f8fafc;
    style Trans fill:#14532d,stroke:#4ade80,stroke-width:2px,color:#f8fafc;
    style Head fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc;
    style Soft fill:#7c2d12,stroke:#fb923c,stroke-width:2px,color:#f8fafc;
    style Sam fill:#581c87,stroke:#c084fc,stroke-width:2px,color:#f8fafc;
    style Loop fill:#334155,stroke:#94a3b8,stroke-width:2px,color:#f8fafc;
    style Out fill:#0f172a,stroke:#4ade80,stroke-width:2px,color:#f8fafc;&lt;/code&gt;&lt;/pre&gt;






&lt;h3&gt;
  
  
  The Tokenization Mechanism: From Words to Numbers
&lt;/h3&gt;

&lt;p&gt;Neural networks cannot process raw characters or words directly; they only perform linear algebra on numbers. &lt;/p&gt;

&lt;h4&gt;
  
  
  Why Not Words or Characters?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Word-level tokenization:&lt;/strong&gt; The vocabulary would be infinite. It would fail completely on typos, hyphenated words, or domain jargon (e.g. &lt;em&gt;"lexicalization"&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Character-level tokenization:&lt;/strong&gt; Sequences become too long (each letter is an input), and individual characters convey minimal semantic meaning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Modern Solution: Subword Tokenization (Byte-Pair Encoding - BPE)
&lt;/h4&gt;

&lt;p&gt;Modern models (like Qwen 2.5 and Llama 3) use &lt;strong&gt;Byte-Pair Encoding (BPE)&lt;/strong&gt;. Frequent words remain single tokens, while rare words are broken into meaningful subword chunks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Original Word&lt;/th&gt;
&lt;th&gt;Subword Breakdown&lt;/th&gt;
&lt;th&gt;Why?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"education"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["education"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Common word; assigned its own dedicated token ID.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"unquestionably"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["un", "question", "ably"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split into common prefix, root, and suffix tokens.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"overemphasizing"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["over", "emphas", "izing"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Common subword stems combined flexibly.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Example: Tokenizing an IELTS Prompt
&lt;/h4&gt;

&lt;p&gt;When you send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IELTS Writing Task 2: Plastic pollution is damaging oceans.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tokenizer consults its dictionary table and produces an array of integer &lt;strong&gt;Token IDs&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38384&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18274&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8431&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27329&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21890&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;374&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21950&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24590&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Special Tokens in ChatML Templates
&lt;/h4&gt;

&lt;p&gt;In conversational models, special delimiter tokens tell the model who is speaking. Without special tokens, the model cannot distinguish between instructions and candidate essays:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;|im_start|&amp;gt;system&lt;/code&gt; : Marks the start of the Senior Examiner rubric persona.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;|im_start|&amp;gt;user&lt;/code&gt; : Marks the candidate essay and prompt.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;|im_start|&amp;gt;assistant&lt;/code&gt; : Signals the model that it is now its turn to generate the score report.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;|im_end|&amp;gt;&lt;/code&gt; : &lt;strong&gt;The Stop Token.&lt;/strong&gt; Signals that generation is complete.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Internal Processing: Embeddings, Positional Encoding &amp;amp; Attention
&lt;/h3&gt;

&lt;p&gt;Once text is tokenized into IDs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Embedding Lookup Matrix (&lt;code&gt;W_e in R^(V * d_model)&lt;/code&gt;):&lt;/strong&gt;
Each token ID is mapped to a continuous, high-dimensional vector. For example, in an 8B model with &lt;code&gt;d_model = 4096&lt;/code&gt;, the integer &lt;code&gt;38384&lt;/code&gt; becomes a vector of 4,096 floating-point numbers encoding its base semantic meaning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotary Positional Embeddings (RoPE):&lt;/strong&gt;
Self-attention on its own is order-agnostic ("dog bites man" would look identical to "man bites dog"). RoPE mathematically rotates the embedding vectors in complex coordinate space so the model knows the exact position and distance of each word in the sequence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Head Self-Attention (The Core Engine):&lt;/strong&gt;
Each token projects into &lt;strong&gt;Query (&lt;code&gt;Q&lt;/code&gt;)&lt;/strong&gt;, &lt;strong&gt;Key (&lt;code&gt;K&lt;/code&gt;)&lt;/strong&gt;, and &lt;strong&gt;Value (&lt;code&gt;V&lt;/code&gt;)&lt;/strong&gt; vectors:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attention(Q, K, V) = softmax((QK^T) / (sqrt(d_k)))V
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the token &lt;code&gt;"congestion"&lt;/code&gt; to attend back to &lt;code&gt;"traffic"&lt;/code&gt; and forward to &lt;code&gt;"delays"&lt;/code&gt;, capturing deep contextual grammar and semantic dependencies across the entire essay.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Prediction Loop: Logits, Softmax &amp;amp; Autoregressive Decoding
&lt;/h3&gt;

&lt;p&gt;LLMs do not produce a whole paragraph in a single forward pass. They are &lt;strong&gt;autoregressive&lt;/strong&gt;: they generate text &lt;strong&gt;one single token at a time&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. From Hidden States to Logits
&lt;/h4&gt;

&lt;p&gt;After passing through all 28–36 transformer layers, the model extracts the final hidden state vector corresponding to the very last token.&lt;/p&gt;

&lt;p&gt;This vector passes through the &lt;strong&gt;Language Model Head (&lt;code&gt;lm_head&lt;/code&gt;)&lt;/strong&gt;, which calculates raw confidence scores (called &lt;strong&gt;logits&lt;/strong&gt;) for each of the 152,000+ words in the model's vocabulary.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. From Logits to Probabilities (Softmax)
&lt;/h4&gt;

&lt;p&gt;Because raw logits can be negative, zero, or arbitrary numbers, the model passes them through the &lt;strong&gt;Softmax function&lt;/strong&gt; to turn them into clean percentages (probabilities) between 0% and 100% that all add up to 1.0 (100%).&lt;/p&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Words with higher logit scores receive higher probabilities.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Temperature (&lt;code&gt;T&lt;/code&gt;)&lt;/strong&gt; setting controls how bold or conservative the model is: a low temperature concentrates probability onto the top word (more focused and deterministic), while a higher temperature spreads probability more evenly (more creative).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Sampling Controls: Temperature, Top-p &amp;amp; Stop Tokens
&lt;/h3&gt;

&lt;p&gt;Once we have a probability distribution over the 152,000 candidate tokens, how do we pick the winner?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Candidate Tokens:
  "###"        -&amp;gt; 64.2%  &amp;lt;-- Most likely next token!
  "Overall"    -&amp;gt; 21.5%
  "The"        -&amp;gt; 8.1%
  "Candidate"  -&amp;gt; 4.3%
  "banana"     -&amp;gt; 0.000001% (Cut off by Top-p)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1. Temperature (&lt;code&gt;T&lt;/code&gt;)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low Temperature (&lt;code&gt;T = 0.1 - 0.3&lt;/code&gt;):&lt;/strong&gt; Sharpen the distribution! The highest probability token becomes overwhelmingly favored. This provides &lt;strong&gt;deterministic, consistent grading&lt;/strong&gt;—essential for IELTS examiner scoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Temperature (&lt;code&gt;T = 0.8 - 1.2&lt;/code&gt;):&lt;/strong&gt; Flatten the distribution! Lower-probability words get a higher chance to be chosen, creating creative, diverse storytelling (but increasing the risk of hallucinations).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greedy Decoding (&lt;code&gt;T = 0&lt;/code&gt;):&lt;/strong&gt; Strictly pick the token with the highest probability (&lt;code&gt;argmax&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Top-p (Nucleus Sampling)
&lt;/h4&gt;

&lt;p&gt;Instead of considering all 152,000 tokens, the model pools only the top tokens whose cumulative probability adds up to &lt;code&gt;p&lt;/code&gt; (e.g., &lt;code&gt;p = 0.90&lt;/code&gt;). The remaining 10% "long tail" of weird or irrelevant words is discarded, eliminating bizarre hallucinations.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Stop Tokens (&lt;code&gt;&amp;lt;|im_end|&amp;gt;&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;How does the generation loop know when to finish?&lt;br&gt;
When the model concludes its final revision paragraph, it predicts the special token &lt;code&gt;&amp;lt;|im_end|&amp;gt;&lt;/code&gt;. The inference engine recognizes this stop token, halts the loop, and triggers the &lt;strong&gt;Detokenizer&lt;/strong&gt; to convert all generated token IDs back into human-readable text on your screen.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Methods of Fine-Tuning
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Full Parameter Fine-Tuning (FPFT)
&lt;/h3&gt;

&lt;p&gt;In Full Parameter Fine-Tuning, every single parameter in the model is updated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; An 8-Billion parameter model stored in 16-bit float requires:

&lt;ul&gt;
&lt;li&gt;Model weights: &lt;code&gt;8 * 2 GB = 16 GB&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Gradient tensors: &lt;code&gt;16 GB&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Optimizer states (AdamW tracks 2 momentum variables per weight): &lt;code&gt;8 * 8 GB = 64 GB&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Activations &amp;amp; buffers: &lt;code&gt;~ 16 GB&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total VRAM required: &amp;gt; 110 GB!&lt;/strong&gt; (Requires multi-A100 server clusters).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Parameter-Efficient Fine-Tuning (PEFT) &amp;amp; LoRA
&lt;/h3&gt;

&lt;p&gt;In 2021, Microsoft researchers published &lt;strong&gt;LoRA (Low-Rank Adaptation)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The core insight: The weight updates &lt;code&gt;ΔW&lt;/code&gt; during domain adaptation have a low "intrinsic dimension" (rank). Instead of updating the full weight matrix &lt;code&gt;W_0 in R^(d * k)&lt;/code&gt;, LoRA decomposes the update into two low-rank matrices &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ΔW = B * A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;B in R^(d * r)&lt;/code&gt; and &lt;code&gt;A in R^(r * k)&lt;/code&gt;, with rank &lt;code&gt;r &amp;lt;&amp;lt; \min(d, k)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Original Weight Matrix W (Frozen)            LoRA Adapter Matrices (Trainable)
        [ d x k ]                                    B [ d x r ]   x   A [ r x k ]
  (Millions of parameters)                           (e.g., r = 16: ~0.5% of parameters)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;During training, &lt;code&gt;W_0&lt;/code&gt; is frozen (requires no optimizer states).&lt;/li&gt;
&lt;li&gt;Only &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; receive gradient updates.&lt;/li&gt;
&lt;li&gt;Memory usage for optimizer states drops by &lt;strong&gt;over 95%&lt;/strong&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  QLoRA: 4-bit Quantized LoRA
&lt;/h3&gt;

&lt;p&gt;Introduced by Dettmers et al. (2023), &lt;strong&gt;QLoRA&lt;/strong&gt; takes LoRA further by combining:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;4-bit NormalFloat (NF4):&lt;/strong&gt; Quantizes the frozen base model weights from 16-bit down to 4-bit without noticeable perplexity degradation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Double Quantization (DQ):&lt;/strong&gt; Quantizes the quantization constants themselves, saving an additional 0.37 bits per parameter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paged Optimizers:&lt;/strong&gt; Uses CUDA Unified Memory to automatically page memory between GPU and CPU RAM during gradient spikes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With QLoRA, an 8-Billion parameter model requires only &lt;strong&gt;~5.5 GB VRAM&lt;/strong&gt; for weights, allowing full fine-tuning on a single &lt;strong&gt;free Google Colab T4 GPU (15 GB VRAM)&lt;/strong&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware Comparison: FPFT vs. LoRA vs. QLoRA
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;Precision of Base Model&lt;/th&gt;
&lt;th&gt;Trainable Parameters&lt;/th&gt;
&lt;th&gt;GPU VRAM Required (8B Model)&lt;/th&gt;
&lt;th&gt;Can run on Free Colab T4?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full Fine-Tuning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;16-bit FP16/BF16&lt;/td&gt;
&lt;td&gt;100% (8.0B)&lt;/td&gt;
&lt;td&gt;~110 GB+&lt;/td&gt;
&lt;td&gt;❌ No (Requires multiple A100s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standard LoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;16-bit FP16/BF16&lt;/td&gt;
&lt;td&gt;~0.5% – 2% (40M)&lt;/td&gt;
&lt;td&gt;~24 GB – 32 GB&lt;/td&gt;
&lt;td&gt;❌ No (Requires A10G / L4 / V100)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QLoRA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4-bit NF4&lt;/td&gt;
&lt;td&gt;~0.5% – 2% (40M)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~7 GB – 10 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Yes (Runs smoothly on T4 15GB)&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  6. Essential Hyperparameters &amp;amp; Micro-Concepts
&lt;/h2&gt;

&lt;p&gt;When beginning LLM fine-tuning, you will encounter parameters and configuration options that look familiar from traditional machine learning, but carry distinct rules of thumb in modern LLM training.&lt;/p&gt;

&lt;h3&gt;
  
  
  Epochs vs. Steps vs. Batch Size
&lt;/h3&gt;

&lt;p&gt;To understand how fine-tuning progresses, you must grasp how data flows into the model:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    Dataset["Full Dataset&amp;lt;br/&amp;gt;(800 Samples)"] --&amp;gt; Split["Split into Mini-Batches"]
    Split --&amp;gt; Step1["Step 1: 8 Samples&amp;lt;br/&amp;gt;Forward + Update"]
    Step1 --&amp;gt; Step2["Step 2: 8 Samples&amp;lt;br/&amp;gt;Forward + Update"]
    Step2 --&amp;gt; Dots["... 100 Total Steps ..."]
    Dots --&amp;gt; EpochEnd["All 800 Seen&amp;lt;br/&amp;gt;= 1 Complete Epoch"]

    style Dataset fill:#0369a1,stroke:#38bdf8,stroke-width:2px,color:#ffffff;
    style Split fill:#1e293b,stroke:#94a3b8,stroke-width:2px,color:#ffffff;
    style Step1 fill:#1e293b,stroke:#64748b,stroke-width:2px,color:#ffffff;
    style Step2 fill:#1e293b,stroke:#64748b,stroke-width:2px,color:#ffffff;
    style Dots fill:#1e293b,stroke:#64748b,stroke-width:2px,color:#ffffff;
    style EpochEnd fill:#15803d,stroke:#4ade80,stroke-width:2px,color:#ffffff;&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Epoch:&lt;/strong&gt; &lt;strong&gt;One complete cycle&lt;/strong&gt; through the entire training dataset. If your dataset has 1,000 examples, 1 epoch means the model has seen all 1,000 examples exactly once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step (Iteration):&lt;/strong&gt; &lt;strong&gt;A single weight update&lt;/strong&gt;. One step consists of:

&lt;ol&gt;
&lt;li&gt;Passing a batch of data through the model (&lt;em&gt;Forward Pass&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Calculating loss and computing gradients (&lt;em&gt;Backward Pass&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Updating the model weights using the optimizer.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The simple calculation formula is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Total Training Steps&lt;/strong&gt; = &lt;code&gt;(Total Examples * Number of Epochs) / Effective Batch Size&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; If you have 800 essays, train for 3 epochs, and use an effective batch size of 8:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Total Steps&lt;/strong&gt; = &lt;code&gt;(800 * 3) / 8 = 300 steps&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Gradient Accumulation &amp;amp; Effective Batch Size
&lt;/h3&gt;

&lt;p&gt;On consumer GPUs like the Colab T4 (15 GB VRAM), setting a physical batch size of 16 or 32 for an 8B model will immediately trigger an &lt;strong&gt;Out-Of-Memory (OOM)&lt;/strong&gt; error. &lt;/p&gt;

&lt;p&gt;However, a tiny batch size (like 1 or 2) makes the gradient updates noisy. How do we solve this? &lt;strong&gt;Gradient Accumulation.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-Device Batch Size (&lt;code&gt;B_device&lt;/code&gt;):&lt;/strong&gt; The number of samples loaded into GPU memory at any single moment (e.g., 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradient Accumulation Steps (&lt;code&gt;G&lt;/code&gt;):&lt;/strong&gt; Instead of updating weights after every single batch, the optimizer computes gradients for &lt;code&gt;G&lt;/code&gt; batches, &lt;em&gt;accumulates (sums) the gradients in memory&lt;/em&gt;, and only updates the weights once every &lt;code&gt;G&lt;/code&gt; steps!&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Effective Batch Size&lt;/strong&gt; = &lt;code&gt;Per-Device Batch Size * Gradient Accumulation Steps * Number of GPUs&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. When is Fine-Tuning Useful? (And When is it Not?)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Best Scenarios for Fine-Tuning
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Domain Assessment &amp;amp; Scoring:&lt;/strong&gt; e.g., IELTS, TOEFL, SAT essays, or coding interview grading adhering to specific rubrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Schema Adherence:&lt;/strong&gt; Enforcing strict, unvarying JSON, YAML, or XML output without preamble or conversational fluff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Vocabulary &amp;amp; Tone:&lt;/strong&gt; Medical triage dialogues, legal clause rewriting, financial auditing tone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency &amp;amp; Edge Deployments:&lt;/strong&gt; Shrinking prompt sizes so a compact model can be deployed locally on laptops with Ollama.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Scenarios Where You Should NOT Fine-Tune
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic / Fast-Changing Knowledge:&lt;/strong&gt; Stock prices, company product catalogs, weather, news. &lt;em&gt;Use RAG instead.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;General Fact Retrieval:&lt;/strong&gt; Fine-tuning on facts frequently induces subtle hallucinations. &lt;em&gt;Use RAG instead.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insufficient Data:&lt;/strong&gt; If you have fewer than 100 examples, few-shot prompting in Claude or GPT-4 will almost always outperform a fine-tuned model.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  8. Summary &amp;amp; What's Next
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Core Concept&lt;/th&gt;
&lt;th&gt;Summary Takeaway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What is it?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adapting model weights to internalize style, structure, and specialized reasoning.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAG vs. FT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RAG brings the facts (open book); Fine-Tuning shapes the behavior and skill (retained mastery).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Epochs &amp;amp; Steps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLMs only need 1–3 epochs; gradient accumulation achieves large effective batch sizes on limited VRAM.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In &lt;strong&gt;&lt;a href="https://dev.to/longtk26/step-by-step-guide-fine-tuning-an-ielts-examiner-llm-with-unsloth-qlora-20a1"&gt;Part 2: Step-by-Step Implementation Guide&lt;/a&gt;&lt;/strong&gt;, we will implement a complete end-to-end training pipeline using &lt;strong&gt;Unsloth&lt;/strong&gt;, &lt;strong&gt;QLoRA&lt;/strong&gt;, and &lt;strong&gt;Google Colab&lt;/strong&gt;, training our own custom &lt;strong&gt;IELTS Examiner AI&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>finetuning</category>
      <category>llm</category>
    </item>
    <item>
      <title>Understanding Observability Stack with Grafana, Prometheus, Loki, and Alloy</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Thu, 27 Aug 2026 17:09:00 +0000</pubDate>
      <link>https://dev.to/longtk26/understanding-observability-stack-with-grafana-prometheus-loki-and-alloy-16p8</link>
      <guid>https://dev.to/longtk26/understanding-observability-stack-with-grafana-prometheus-loki-and-alloy-16p8</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Understanding Observability Stack with Grafana, Prometheus, Loki, and Alloy

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;How Observability Components Fit Together&lt;/li&gt;
&lt;li&gt;Simple Data Flow&lt;/li&gt;
&lt;li&gt;Core Stack Components &amp;amp; Their Usages&lt;/li&gt;
&lt;li&gt;1. Prometheus: Metrics Database&lt;/li&gt;
&lt;li&gt;2. Grafana Loki: Logs Database&lt;/li&gt;
&lt;li&gt;3. Grafana: Dashboards &amp;amp; Visualization&lt;/li&gt;
&lt;li&gt;4. Exporters: System Metric Translators&lt;/li&gt;
&lt;li&gt;5. Grafana Alloy: Telemetry Collector&lt;/li&gt;
&lt;li&gt;Component Summary Table&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When you deploy applications to a server, you need a reliable way to know if everything is running smoothly. This is where &lt;strong&gt;observability&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;Observability helps you answer simple but essential questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the server running out of CPU or memory?&lt;/li&gt;
&lt;li&gt;How many requests per second is the API handling?&lt;/li&gt;
&lt;li&gt;Why did a user get an error, and what exact line in the logs explains it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a modern observability stack, telemetry is primarily divided into two main types of data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt;: Numerical values recorded over time (e.g., CPU usage %, memory in MB, request count, response time).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt;: Timestamped text messages output by applications or the operating system (e.g., error stack traces, informational events).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To collect, store, and visualize this data, we combine five core open-source components: &lt;strong&gt;Prometheus&lt;/strong&gt;, &lt;strong&gt;Grafana Loki&lt;/strong&gt;, &lt;strong&gt;Grafana&lt;/strong&gt;, &lt;strong&gt;Exporters&lt;/strong&gt;, and &lt;strong&gt;Grafana Alloy&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Observability Components Fit Together
&lt;/h2&gt;

&lt;p&gt;Here is a simple overview of how telemetry data moves from your applications all the way to your browser:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Simple Data Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Emit Data&lt;/strong&gt;: Applications and system exporters produce metrics and log messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collect&lt;/strong&gt;: &lt;strong&gt;Grafana Alloy&lt;/strong&gt; runs locally, gathers the metrics and logs, adds useful labels (such as &lt;code&gt;app="my-api"&lt;/code&gt;), and forwards them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store&lt;/strong&gt;: 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt; receives and stores the numerical metrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loki&lt;/strong&gt; receives and stores the log entries.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualize&lt;/strong&gt;: &lt;strong&gt;Grafana&lt;/strong&gt; connects to both Prometheus and Loki, allowing you to view graphs, inspect logs, and set up alerts on a single screen.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Core Stack Components &amp;amp; Their Usages
&lt;/h2&gt;

&lt;p&gt;Let's break down each component and what it is used for.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Prometheus: Metrics Database
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; is an open-source database built specifically for &lt;strong&gt;time-series metrics&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does&lt;/strong&gt;: Stores numbers paired with timestamps and labels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example Data&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  http_requests_total{app="web", status="200"} 1540
  cpu_usage_percentage{host="server-1"} 42.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How you query it&lt;/strong&gt;: Uses &lt;strong&gt;PromQL&lt;/strong&gt; (Prometheus Query Language) to compute rates, averages, and totals over time:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  rate(http_requests_total[5m])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Main Usage&lt;/strong&gt;: Measuring CPU/RAM usage, request throughput, error counts, and response latency.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Grafana Loki: Logs Database
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://grafana.com/oss/loki/" rel="noopener noreferrer"&gt;Grafana Loki&lt;/a&gt; is an open-source log aggregation system designed by Grafana.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does&lt;/strong&gt;: Stores text log streams from all your applications and containers in one searchable place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Advantage&lt;/strong&gt;: Unlike traditional search engines that index every single word, Loki indexes only the &lt;strong&gt;labels&lt;/strong&gt; (like &lt;code&gt;app="backend"&lt;/code&gt;, &lt;code&gt;env="prod"&lt;/code&gt;). This makes it fast, lightweight, and very cheap on disk and memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How you query it&lt;/strong&gt;: Uses &lt;strong&gt;LogQL&lt;/strong&gt; to search and filter log lines:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {app="backend"} |= "error"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Main Usage&lt;/strong&gt;: Searching application logs, investigating exceptions, and reviewing debug traces.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Grafana: Dashboards &amp;amp; Visualization
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; is the web UI where you view and interact with all your monitoring data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does&lt;/strong&gt;: Connects to data sources like Prometheus and Loki to render graphs, charts, counters, and log panels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Features&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unified Dashboards&lt;/strong&gt;: Display CPU graphs, database queries, and live error logs side-by-side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerting&lt;/strong&gt;: Sends notifications to Slack, Discord, or email when thresholds are exceeded (e.g., when CPU &amp;gt; 90%).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore Mode&lt;/strong&gt;: Quickly run ad-hoc queries without needing to create a permanent dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main Usage&lt;/strong&gt;: The central control room for developers and operators to monitor system health.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Exporters: System Metric Translators
&lt;/h3&gt;

&lt;p&gt;Software like Linux OS, PostgreSQL, or Redis does not natively output Prometheus metrics. &lt;strong&gt;Exporters&lt;/strong&gt; act as translators: they read system stats and expose them as a &lt;code&gt;/metrics&lt;/code&gt; page that Prometheus can read.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node Exporter&lt;/strong&gt;: Measures host-level stats (OS CPU, RAM, disk space, network traffic).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cAdvisor (Container Advisor)&lt;/strong&gt;: Measures Docker container stats (per-container CPU, RAM limits, container status).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Exporters (Postgres / Redis Exporters)&lt;/strong&gt;: Measure active database connections, query throughput, and cache hit rates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main Usage&lt;/strong&gt;: Providing visibility into servers, containers, and databases without modifying their source code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5. Grafana Alloy: Telemetry Collector
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://grafana.com/docs/alloy/latest/" rel="noopener noreferrer"&gt;Grafana Alloy&lt;/a&gt; is Grafana's modern, lightweight telemetry agent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What it does&lt;/strong&gt;: Runs on the server where your applications live. It gathers &lt;code&gt;/metrics&lt;/code&gt; from apps and exporters, tails log files from Docker containers, and pushes everything to Prometheus and Loki.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it is useful&lt;/strong&gt;: Instead of installing separate tools for metrics and logs (like older setups with Promtail and Prometheus Agent), Alloy handles both in a single lightweight program.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Main Usage&lt;/strong&gt;: Local scraping, enriching data with common labels, and shipping telemetry to the databases.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Component Summary Table
&lt;/h2&gt;

&lt;p&gt;Here is a quick summary of all five components:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;Telemetry Type&lt;/th&gt;
&lt;th&gt;Query Language&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prometheus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metrics Database&lt;/td&gt;
&lt;td&gt;Stores numerical measurements over time&lt;/td&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;PromQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grafana Loki&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logs Database&lt;/td&gt;
&lt;td&gt;Stores and indexes log streams&lt;/td&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;LogQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grafana&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Visualization UI&lt;/td&gt;
&lt;td&gt;Displays charts, graphs, and sends alerts&lt;/td&gt;
&lt;td&gt;Dashboards &amp;amp; Alerts&lt;/td&gt;
&lt;td&gt;N/A (Uses PromQL &amp;amp; LogQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exporters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metric Translators&lt;/td&gt;
&lt;td&gt;Reads OS, Docker, and DB stats and outputs metrics&lt;/td&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;N/A (HTTP &lt;code&gt;/metrics&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grafana Alloy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Telemetry Collector&lt;/td&gt;
&lt;td&gt;Scrapes metrics and tails logs, then pushes to databases&lt;/td&gt;
&lt;td&gt;Metrics &amp;amp; Logs&lt;/td&gt;
&lt;td&gt;N/A (Agent Configuration)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;I hope this blog is helpful to you.&lt;br&gt;
Understanding these five fundamental building blocks, you can get started with modern observability. Thank you for reading.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>grafana</category>
      <category>prometheus</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Overview: Change Data Capture (CDC)</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Fri, 17 Apr 2026 14:03:41 +0000</pubDate>
      <link>https://dev.to/longtk26/overview-change-data-capture-cdc-4d42</link>
      <guid>https://dev.to/longtk26/overview-change-data-capture-cdc-4d42</guid>
      <description>&lt;h1&gt;
  
  
  Overview: Change Data Capture (CDC)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Welcome! In this post, we'll explore &lt;strong&gt;Change Data Capture (CDC)&lt;/strong&gt; — one of those foundational concepts that can genuinely transform the way you think about data synchronization in distributed systems. Let's dive in!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is Change Data Capture?&lt;/li&gt;
&lt;li&gt;
Use Cases

&lt;ul&gt;
&lt;li&gt;Cache Invalidation&lt;/li&gt;
&lt;li&gt;Data Integration&lt;/li&gt;
&lt;li&gt;Real-Time Analysis&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Pros and Cons

&lt;ul&gt;
&lt;li&gt;Pros&lt;/li&gt;
&lt;li&gt;Cons&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;When Should You Use CDC?&lt;/li&gt;

&lt;li&gt;When Should You NOT Use CDC?&lt;/li&gt;

&lt;li&gt;Wrapping Up&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Change Data Capture?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Change Data Capture (CDC)&lt;/strong&gt; is a method used in databases to track and record changes made to data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CDC captures modifications like inserts, updates, and deletes, and makes them available for downstream systems to consume — either for analysis, replication, or synchronization purposes. CDC helps maintain data consistency across different systems by recording alterations in real time.&lt;/p&gt;

&lt;p&gt;Think of it like a digital detective sitting quietly inside your database, logging every change that happens and when it occurred — so that other systems never fall behind.&lt;/p&gt;




&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cache Invalidation
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Automatically invalidate entries in a cache as soon as the corresponding record changes or is removed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your cache (such as Redis, Memcached, or Infinispan) runs as a separate process, the invalidation logic can be placed into a dedicated service — completely decoupled from your main application. In more advanced setups, you can even use the data from the change event itself to update the affected cache entries directly, rather than just evicting them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Integration
&lt;/h3&gt;

&lt;p&gt;Data is often stored in multiple places, especially when used for different purposes or in slightly different shapes. Keeping those multiple systems in sync can quickly become a maintenance nightmare.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; When a system uses a separate search service like Elasticsearch, it's critical to keep the search index synchronized with the primary database whenever records are created or updated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CDC makes this elegant: changes are captured from the database and automatically propagated to the search service. Instead of sprinkling Elasticsearch update calls throughout your business logic, a lightweight event-processing layer handles it asynchronously. This simplifies the application design while improving scalability and maintainability.&lt;/p&gt;

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

&lt;p&gt;CDC powers real-time analytics platforms by continuously feeding data changes into analytical systems. Organizations can then derive insights from fresh data and respond swiftly to changing conditions — all without expensive batch jobs or manual ETL pipelines.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Decouples Application Logic
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The main application no longer needs to explicitly publish events or sync data to downstream systems.&lt;/li&gt;
&lt;li&gt;Reduces duplicated logic across services — one source of truth emits events, and consumers react independently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Near Real-Time Data Synchronization
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Changes from the database are streamed almost instantly.&lt;/li&gt;
&lt;li&gt;Ideal for search indexing, analytics pipelines, and cache management.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Non-Intrusive to Existing Systems
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Works at the database level (via WAL / binlog), so there's usually no need to heavily modify existing application code.&lt;/li&gt;
&lt;li&gt;Particularly useful for integrating with legacy systems where source code changes are risky or costly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Event-Driven Architecture Enablement
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Naturally integrates with streaming platforms like Apache Kafka.&lt;/li&gt;
&lt;li&gt;Makes it easier to build reactive microservices and real-time data pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. Data Consistency (Eventually Consistent)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Ensures downstream systems gradually converge toward the source of truth over time.&lt;/li&gt;
&lt;li&gt;Change events are durable and ordered, making them reliable for replication.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6. Supports Multiple Consumers
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;One stream of change events can feed multiple services simultaneously — search indexing, analytics, auditing, cache invalidation, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Increased System Complexity
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Requires additional infrastructure: a message broker (e.g., Kafka), connectors, and monitoring tooling.&lt;/li&gt;
&lt;li&gt;Debugging issues that span multiple components can be significantly harder than debugging a simple API call.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Schema Evolution Challenges
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Changes in the database schema (adding/removing columns, renaming tables) can break downstream consumers if not handled carefully.&lt;/li&gt;
&lt;li&gt;A solid schema management strategy — such as a schema registry — becomes essential at scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Operational Overhead
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Someone needs to manage connectors, track offsets, handle failures, and configure retries.&lt;/li&gt;
&lt;li&gt;This requires a level of DevOps maturity that smaller teams may not yet have.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Learning Curve
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Teams need to invest time in understanding event-driven design principles, CDC concepts, and the tooling involved.&lt;/li&gt;
&lt;li&gt;The mental model shift from "call an API" to "react to events" can take time to internalize.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. Data Filtering and Transformation Are Limited at the Source
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Raw database change events may not perfectly match what downstream consumers need.&lt;/li&gt;
&lt;li&gt;An additional transformation or processing layer is often required to shape, filter, or enrich events before they reach their destination.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When Should You Use CDC?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use CDC when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need to sync data to systems like Elasticsearch, a data warehouse, or a cache layer&lt;/li&gt;
&lt;li&gt;You are building an event-driven or microservices architecture&lt;/li&gt;
&lt;li&gt;You want to reduce coupling between services&lt;/li&gt;
&lt;li&gt;You need audit logs or historical change tracking&lt;/li&gt;
&lt;li&gt;You have multiple downstream consumers of the same data&lt;/li&gt;
&lt;li&gt;You want near real-time data propagation without modifying core application logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;** Examples:**&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sync user records from PostgreSQL → Elasticsearch for full-text search&lt;/li&gt;
&lt;li&gt;Stream order events into an analytics pipeline for real-time dashboards&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When Should You NOT Use CDC?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Avoid CDC when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your system requires &lt;strong&gt;strong consistency&lt;/strong&gt; — CDC is eventually consistent and introduces some propagation delay&lt;/li&gt;
&lt;li&gt;The system is &lt;strong&gt;simple or monolithic&lt;/strong&gt; and doesn't benefit from event streaming&lt;/li&gt;
&lt;li&gt;You only have &lt;strong&gt;one consumer&lt;/strong&gt; and a direct API call or a database trigger would suffice&lt;/li&gt;
&lt;li&gt;Your team lacks experience in operating distributed systems&lt;/li&gt;
&lt;li&gt;Infrastructure cost and operational complexity are a concern&lt;/li&gt;
&lt;li&gt;Your database is already under heavy load (CDC adds overhead via WAL reading)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A simple CRUD app with basic search — just update Elasticsearch directly from the application&lt;/li&gt;
&lt;li&gt;A low-scale system with no real-time data requirements&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;CDC is a powerful pattern, but like any tool, it shines brightest when applied to the right problem. If you are building systems where data needs to flow reliably across multiple components in near real time, CDC is absolutely worth the investment.&lt;/p&gt;

&lt;p&gt;Thank you so much for reading! I hope this overview gave you a clear and practical understanding of Change Data Capture. If you found this helpful, feel free to explore the &lt;a href="https://dev.to/longtk26/debezium-cdc-in-practice-1h1e"&gt;Debezium setup guide&lt;/a&gt; to see how to put these concepts into practice with real infrastructure. Happy building! 🚀&lt;/p&gt;

</description>
      <category>database</category>
      <category>dataengineering</category>
      <category>distributedsystems</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Debezium: CDC in Practice</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Fri, 17 Apr 2026 14:03:35 +0000</pubDate>
      <link>https://dev.to/longtk26/debezium-cdc-in-practice-1h1e</link>
      <guid>https://dev.to/longtk26/debezium-cdc-in-practice-1h1e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;If you've read the &lt;a href="https://dev.to/longtk26/overview-change-data-capture-cdc-4d42"&gt;Overview CDC&lt;/a&gt; post, you already know &lt;em&gt;why&lt;/em&gt; Change Data Capture is such a compelling pattern. Now it's time to get our hands dirty. In this guide, we'll walk through a complete, working Debezium setup — from standing up Kafka and Debezium Connect with Docker, all the way to watching live database changes flow into Elasticsearch in real time. Let's go! 🛠️&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is Debezium?&lt;/li&gt;
&lt;li&gt;
Setup: PostgreSQL Connector for Tracking Database Changes

&lt;ul&gt;
&lt;li&gt;Step 1 – Create the Docker Network&lt;/li&gt;
&lt;li&gt;Step 2 – Prepare the Custom Debezium Connect Image&lt;/li&gt;
&lt;li&gt;Step 3 – Prepare the Docker Compose File&lt;/li&gt;
&lt;li&gt;Step 4 – Start the Services&lt;/li&gt;
&lt;li&gt;Step 5 – Configure the PostgreSQL Connector via Debezium UI&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Setup: Synchronization from PostgreSQL to Elasticsearch

&lt;ul&gt;
&lt;li&gt;Step 1 – Start Elasticsearch and Kibana&lt;/li&gt;
&lt;li&gt;Step 2 – Register the Connectors&lt;/li&gt;
&lt;li&gt;Step 3 – Verify the Pipeline&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Wrapping Up&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Debezium?
&lt;/h2&gt;

&lt;p&gt;Debezium is an open-source project that provides a &lt;strong&gt;low-latency data streaming platform for Change Data Capture (CDC)&lt;/strong&gt;. It connects to your database, monitors its transaction log (e.g., PostgreSQL's Write-Ahead Log), and emits a stream of change events for every row-level modification.&lt;/p&gt;

&lt;p&gt;Your applications then consume these events and react accordingly — syncing to a search index, invalidating a cache, updating a data warehouse, or triggering downstream workflows.&lt;/p&gt;

&lt;p&gt;Key characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; under the Apache 2.0 license&lt;/li&gt;
&lt;li&gt;Built on top of &lt;strong&gt;Kafka Connect&lt;/strong&gt; — battle-tested, scalable, and extensible&lt;/li&gt;
&lt;li&gt;Supports many popular databases: &lt;strong&gt;PostgreSQL&lt;/strong&gt;, MySQL, MongoDB, SQL Server, Oracle, and more&lt;/li&gt;
&lt;li&gt;Guarantees &lt;strong&gt;at-least-once delivery&lt;/strong&gt; of change events with configurable offset management&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Setup: PostgreSQL Connector for Tracking Database Changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 – Create the Docker Network
&lt;/h3&gt;

&lt;p&gt;All services in this setup share a common Docker bridge network. Create it once before starting any containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create base-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 – Prepare the Custom Debezium Connect Image
&lt;/h3&gt;

&lt;p&gt;We need a custom Debezium Connect image that includes the &lt;strong&gt;Confluent Elasticsearch Sink Connector&lt;/strong&gt; plugin. Create a &lt;code&gt;Dockerfile&lt;/code&gt; inside a folder named &lt;code&gt;connect/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; debezium/connect:3.0.0.Final&lt;/span&gt;

&lt;span class="c"&gt;# Install the Confluent Elasticsearch Sink Connector for Kafka Connect.&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; ELASTICSEARCH_SINK_VERSION=15.1.1&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;
    curl -fsSL "https://hub-downloads.confluent.io/api/plugins/confluentinc/kafka-connect-elasticsearch/versions/${ELASTICSEARCH_SINK_VERSION}/confluentinc-kafka-connect-elasticsearch-${ELASTICSEARCH_SINK_VERSION}.zip" -o /tmp/es-sink.zip; \

    mkdir -p /kafka/connect/confluentinc-kafka-connect-elasticsearch; \

    unzip -q /tmp/es-sink.zip -d /kafka/connect/confluentinc-kafka-connect-elasticsearch; \

    rm -f /tmp/es-sink.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you don't need Elasticsearch, skip the custom image and use the official &lt;code&gt;debezium/connect:3.0.0.Final&lt;/code&gt; image directly in your Compose file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 3 – Prepare the Docker Compose File
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;docker-compose.yml&lt;/code&gt; at the project root with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.9'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kafka-ui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kafka-ui&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/kafbat/kafka-ui:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8070:8080"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kafka&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_CLUSTERS_0_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kafka:9092&lt;/span&gt;
      &lt;span class="na"&gt;DYNAMIC_CONFIG_ENABLED&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;base-network&lt;/span&gt;

  &lt;span class="na"&gt;kafka&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache/kafka:4.0.2&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kafka&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9092:9092"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9094:9094"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

      &lt;span class="c1"&gt;############################################&lt;/span&gt;
      &lt;span class="c1"&gt;# KRaft Metadata &amp;amp; Node Identity&lt;/span&gt;
      &lt;span class="c1"&gt;############################################&lt;/span&gt;

      &lt;span class="c1"&gt;# Unique ID for this Kafka node&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_NODE_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

      &lt;span class="c1"&gt;# Unique Kafka cluster identifier (required for KRaft mode)&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_CLUSTER_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;energy-tracker-cluster-1'&lt;/span&gt;

      &lt;span class="c1"&gt;# Node roles — this instance acts as BOTH a broker and a controller&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_PROCESS_ROLES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;broker,controller'&lt;/span&gt;

      &lt;span class="c1"&gt;############################################&lt;/span&gt;
      &lt;span class="c1"&gt;# Controller Quorum Configuration (Raft)&lt;/span&gt;
      &lt;span class="c1"&gt;############################################&lt;/span&gt;

      &lt;span class="c1"&gt;# List of controller nodes that form the Raft quorum&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_CONTROLLER_QUORUM_VOTERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1@kafka:9093'&lt;/span&gt;

      &lt;span class="c1"&gt;# Listener name used by controllers for internal Raft communication&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_CONTROLLER_LISTENER_NAMES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CONTROLLER'&lt;/span&gt;

      &lt;span class="c1"&gt;############################################&lt;/span&gt;
      &lt;span class="c1"&gt;# Network Listeners (Internal, External &amp;amp; Controller)&lt;/span&gt;
      &lt;span class="c1"&gt;############################################&lt;/span&gt;

      &lt;span class="c1"&gt;# Define all listener endpoints for this Kafka node&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_LISTENERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="s"&gt;PLAINTEXT://0.0.0.0:9092,&lt;/span&gt;
        &lt;span class="s"&gt;EXTERNAL://0.0.0.0:9094,&lt;/span&gt;
        &lt;span class="s"&gt;CONTROLLER://0.0.0.0:9093&lt;/span&gt;

      &lt;span class="c1"&gt;# What each listener advertises to connecting clients&lt;/span&gt;
      &lt;span class="c1"&gt;# Internal (Docker containers): kafka:9092&lt;/span&gt;
      &lt;span class="c1"&gt;# External (host machine): localhost:9094&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_ADVERTISED_LISTENERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="s"&gt;PLAINTEXT://kafka:9092,&lt;/span&gt;
        &lt;span class="s"&gt;EXTERNAL://localhost:9094&lt;/span&gt;

      &lt;span class="c1"&gt;# Maps listener names to their security protocols&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_LISTENER_SECURITY_PROTOCOL_MAP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="s"&gt;CONTROLLER:PLAINTEXT,&lt;/span&gt;
        &lt;span class="s"&gt;PLAINTEXT:PLAINTEXT,&lt;/span&gt;
        &lt;span class="s"&gt;EXTERNAL:PLAINTEXT&lt;/span&gt;

      &lt;span class="c1"&gt;# Which listener brokers use for internal broker-to-broker communication&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_INTER_BROKER_LISTENER_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PLAINTEXT'&lt;/span&gt;

      &lt;span class="c1"&gt;############################################&lt;/span&gt;
      &lt;span class="c1"&gt;# Single-Node Cluster Safety Settings&lt;/span&gt;
      &lt;span class="c1"&gt;# (replication factor &amp;gt; 1 would fail on a single node)&lt;/span&gt;
      &lt;span class="c1"&gt;############################################&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_TRANSACTION_STATE_LOG_MIN_ISR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

      &lt;span class="c1"&gt;############################################&lt;/span&gt;
      &lt;span class="c1"&gt;# Broker Defaults &amp;amp; Quality-of-Life Settings&lt;/span&gt;
      &lt;span class="c1"&gt;############################################&lt;/span&gt;

      &lt;span class="c1"&gt;# Speed up consumer group startup&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="c1"&gt;# Auto-create topics if they don't already exist&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_AUTO_CREATE_TOPICS_ENABLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;

      &lt;span class="c1"&gt;# Default number of partitions per topic&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_NUM_PARTITIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;base-network&lt;/span&gt;

  &lt;span class="na"&gt;debezium-connect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./connect&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debezium-connect&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8083:8083"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;BOOTSTRAP_SERVERS=kafka:9092&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GROUP_ID=1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CONFIG_STORAGE_TOPIC=my_connect_configs&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;OFFSET_STORAGE_TOPIC=my_connect_offsets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;STATUS_STORAGE_TOPIC=my_connect_statuses&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CONFIG_STORAGE_REPLICATION_FACTOR=1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;OFFSET_STORAGE_REPLICATION_FACTOR=1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;STATUS_STORAGE_REPLICATION_FACTOR=1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REST_ADVERTISED_HOST_NAME=debezium-connect&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KEY_CONVERTER=org.apache.kafka.connect.storage.StringConverter&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;VALUE_CONVERTER=org.apache.kafka.connect.json.JsonConverter&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;VALUE_CONVERTER_SCHEMAS_ENABLE=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PLUGIN_PATH=/kafka/connect,/usr/share/java,/usr/share/confluent-hub-components&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kafka&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;base-network&lt;/span&gt;

  &lt;span class="na"&gt;debezium-ui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debezium-ui&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debezium/debezium-ui&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KAFKA_CONNECT_URIS=http://debezium-connect:8083&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;debezium-connect&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;base-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;base-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Service summary:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debezium-connect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hosts the Kafka Connect worker. Connectors registered here read the PostgreSQL WAL and publish change events to Kafka topics.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debezium-ui&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A web UI that simplifies connector management by wrapping the Debezium Connect REST API.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kafka&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The message broker that durably stores change event streams. Runs in KRaft mode (no Zookeeper required).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kafka-ui&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A web UI for browsing Kafka topics, messages, and consumer groups — invaluable for debugging.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 4 – Start the Services
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the containers are healthy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debezium UI&lt;/strong&gt; → &lt;code&gt;http://localhost:8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kafka UI&lt;/strong&gt; → &lt;code&gt;http://localhost:8070&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5 – Configure the PostgreSQL Connector via Debezium UI
&lt;/h3&gt;

&lt;p&gt;Before creating the connector, make sure you have a &lt;code&gt;users&lt;/code&gt; table in your PostgreSQL database.&lt;/p&gt;

&lt;p&gt;Open Debezium UI at &lt;code&gt;http://localhost:8080&lt;/code&gt; and follow the steps shown in the screenshots below to create a new PostgreSQL source connector:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Create Connector&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Choose the &lt;strong&gt;PostgreSQL&lt;/strong&gt; connector type&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Fill in the basic connection settings (hostname, port, database name, credentials)&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Configure advanced settings (table filter, plugin name: &lt;code&gt;pgoutput&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Validate the configuration and finish&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Confirm the connector is running&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Now try inserting a record into the &lt;code&gt;users&lt;/code&gt; table and open Kafka UI at &lt;code&gt;http://localhost:8070&lt;/code&gt; to see the change event appear on the Kafka topic:&lt;/p&gt;

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

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




&lt;h2&gt;
  
  
  Setup: Synchronization from PostgreSQL to Elasticsearch
&lt;/h2&gt;

&lt;p&gt;With Debezium already streaming changes to Kafka, adding Elasticsearch as a sync target is straightforward — we just need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start an Elasticsearch (+ Kibana) stack&lt;/li&gt;
&lt;li&gt;Register a sink connector that reads from the Kafka topic and writes to an Elasticsearch index&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1 – Start Elasticsearch and Kibana
&lt;/h3&gt;

&lt;p&gt;Create a separate &lt;code&gt;docker-compose-elk.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;elasticsearch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.elastic.co/elasticsearch/elasticsearch:7.17.15&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;elasticsearch&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9200:9200"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;discovery.type=single-node&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xpack.security.enabled=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ES_JAVA_OPTS=-Xms512m -Xmx512m&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;base-network&lt;/span&gt;

  &lt;span class="na"&gt;kibana&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.elastic.co/kibana/kibana:7.17.15&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kibana&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5601:5601"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ELASTICSEARCH_HOSTS=http://elasticsearch:9200&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;elasticsearch&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;base-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;base-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose-elk.yml up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch&lt;/strong&gt; → &lt;code&gt;http://localhost:9200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kibana&lt;/strong&gt; → &lt;code&gt;http://localhost:5601&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2 – Register the Connectors
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure the &lt;code&gt;users&lt;/code&gt; table exists in your PostgreSQL database before registering the connectors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use the following &lt;code&gt;curl&lt;/code&gt; commands to register both connectors via the Debezium Connect REST API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Elasticsearch Sink Connector&lt;/strong&gt; — reads the &lt;code&gt;users&lt;/code&gt; Kafka topic and writes to the &lt;code&gt;users&lt;/code&gt; Elasticsearch index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:8083/connectors'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "name": "elasticsearch-sink-connector",
  "config": {
    "connector.class": "io.confluent.connect.elasticsearch.ElasticsearchSinkConnector",
    "topics": "users",
    "connection.url": "http://elasticsearch:9200",

    "key.converter": "org.apache.kafka.connect.json.JsonConverter",
    "key.converter.schemas.enable": "false",
    "value.converter": "org.apache.kafka.connect.json.JsonConverter",
    "value.converter.schemas.enable": "false",

    "schema.ignore": "true",
    "key.ignore": "false",

    "write.method": "upsert",
    "behavior.on.null.values": "delete",

    "transforms": "extractKey,removeDeleted",
    "transforms.extractKey.type": "org.apache.kafka.connect.transforms.ExtractField$Key",
    "transforms.extractKey.field": "id",

    "transforms.removeDeleted.type": "org.apache.kafka.connect.transforms.ReplaceField$Value",
    "transforms.removeDeleted.blacklist": "__deleted",

    "errors.tolerance": "all",
    "errors.deadletterqueue.topic.name": "dlq-users",
    "errors.deadletterqueue.topic.replication.factor": "1"
  }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PostgreSQL Source Connector&lt;/strong&gt; — reads WAL changes from PostgreSQL and publishes them to the &lt;code&gt;users&lt;/code&gt; Kafka topic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:8083/connectors'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{
  "name": "postgres-source-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname": "postgresdb",
    "database.port": "5432",
    "database.user": "postgres",
    "database.password": "postgres",
    "database.dbname": "template",
    "topic.prefix": "users_table",
    "table.include.list": "public.users",
    "plugin.name": "pgoutput",
    "key.converter": "org.apache.kafka.connect.json.JsonConverter",
    "key.converter.schemas.enable": "false",
    "value.converter": "org.apache.kafka.connect.json.JsonConverter",
    "value.converter.schemas.enable": "false",
    "transforms": "route,unwrap",
    "transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter",
    "transforms.route.regex": "users_table.public.users",
    "transforms.route.replacement": "users",
    "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
    "transforms.unwrap.drop.tombstones": "false",
    "transforms.unwrap.delete.handling.mode": "rewrite"
  }
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 – Verify the Pipeline
&lt;/h3&gt;

&lt;p&gt;After registering both connectors, check their status in Debezium UI and verify that a consumer group has been created in Kafka UI:&lt;/p&gt;

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

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

&lt;p&gt;Now run some INSERT, UPDATE, or DELETE statements on the &lt;code&gt;users&lt;/code&gt; table in PostgreSQL and follow the data through the pipeline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Run a search in Kibana DevTools before inserting any data:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;2. Insert a new record into the &lt;code&gt;users&lt;/code&gt; table:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;3. Check the &lt;code&gt;users&lt;/code&gt; topic in Kafka UI — the change event should appear:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;4. Query the &lt;code&gt;users&lt;/code&gt; index in Elasticsearch via Kibana DevTools — the new document should be there:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;That's the full pipeline working end-to-end: PostgreSQL → Debezium → Kafka → Elasticsearch. 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/longtk26/setup-cdc" rel="noopener noreferrer"&gt;Setup CDC&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;You've just set up a fully functional CDC pipeline with Debezium! From a single row update in PostgreSQL, a change event flows through Kafka and lands in Elasticsearch — all automatically, with zero application code involved.&lt;/p&gt;

&lt;p&gt;This is just the beginning. From here you can explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding more sink connectors (e.g., a data warehouse, another database)&lt;/li&gt;
&lt;li&gt;Schema evolution strategies using a schema registry&lt;/li&gt;
&lt;li&gt;Monitoring connector health and lag in production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you so much for following along! I hope this guide saved you hours of trial and error. If you haven't read it yet, the &lt;a href="https://dev.to/longtk26/overview-change-data-capture-cdc-4d42"&gt;Overview CDC&lt;/a&gt; post provides great background context on the concepts behind what we built here. Happy streaming! 🚀&lt;/p&gt;

</description>
      <category>database</category>
      <category>docker</category>
      <category>postgres</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Subtitle to Audio Tutorial</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Sun, 18 Jan 2026 16:17:50 +0000</pubDate>
      <link>https://dev.to/longtk26/subtitle-to-audio-tutorial-19go</link>
      <guid>https://dev.to/longtk26/subtitle-to-audio-tutorial-19go</guid>
      <description>&lt;h1&gt;
  
  
  Subtitle to Audio Tutorial
&lt;/h1&gt;

&lt;p&gt;Hi everyone! In this tutorial, we will explore how to convert subtitles into audio using text-to-speech (TTS) technology. This process can be particularly useful for creating audiobooks, podcasts, or enhancing accessibility for video content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Subtitle to Audio Tutorial

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Workflow Overview&lt;/li&gt;
&lt;li&gt;
Problems with workflow

&lt;ul&gt;
&lt;li&gt;1. Limited execution time of lambda functions&lt;/li&gt;
&lt;li&gt;2. Limited resources within lambda functions&lt;/li&gt;
&lt;li&gt;3. Synchronous time and speed between subtile and audio&lt;/li&gt;
&lt;li&gt;4. Popping noises between audio segments&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Solutions

&lt;ul&gt;
&lt;li&gt;Algorithm diagram&lt;/li&gt;
&lt;li&gt;Key Optimizations&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;When working with applications that involve video content and translation, it is often necessary to translate an original subtitle file (e.g., SRT format) into another language.&lt;/li&gt;
&lt;li&gt;To enhance user experience, we can convert these translated subtitles into audio using TTS technology.&lt;/li&gt;
&lt;li&gt;This tutorial will guide you through the process of converting subtitles to audio, addressing common challenges and providing solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Workflow Overview
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;Convert from subtitle to audio is a job that need several steps to complete that why we will handle it asynchronously.&lt;/li&gt;
&lt;li&gt;We choose lamba functions for saving cost and easy in implementation.&lt;/li&gt;
&lt;li&gt;The workflow consist of the following steps:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choose a subtitle file from current system&lt;/strong&gt;: Assume our system already show list of subtitle files that user can choose from.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose voice settings&lt;/strong&gt;: User can choose voice settings like male or female voice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request to convert subtitle to audio&lt;/strong&gt;: After user choose subtitle file and voice settings we will send request to backend to start the conversion process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server request to s3 to get subtitle file&lt;/strong&gt;: Because in real project we usually store files in s3, so our server need to get subtitle file url from s3 first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server request to lambda function to convert subtitle to audio&lt;/strong&gt;: After get subtitle file url server will send request to lambda function to start convert subtitle to audio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda function process subtitle file&lt;/strong&gt;: Lambda function will download subtitle file from s3, parse subtitle file to get text and timing information, then use google text to speech to convert text to audio, finally combine audio segments according to timing information and save the final audio file to s3.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Problems with workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Limited execution time of lambda functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda functions have a maximum execution time limit (currently 15 minutes). If the subtitle file is large or the conversion process takes too long, the lambda function may time out before completing the task.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Limited resources within lambda functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Lambda functions have limited CPU and memory resources. If the TTS engine requires more resources or inprogress the size of audio become to large, the conversion process may fail or produce error out of memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Synchronous time and speed between subtile and audio
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensuring that the audio segments align perfectly with the timing information in the subtitle file can be challenging. Any discrepancies can lead to audio that is out of sync with the original video content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Popping noises between audio segments
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When combining multiple audio segments generated from subtitles, popping noises may occur at the boundaries between segments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;p&gt;This section will show you solutions for the problems mentioned above and successully in converting subtitle file more than 30 minutes to audio only in some minutes. Also implement it with at least resources, we only use default lambda function settings (128MB memory) and timeout is 15 minutes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Algorithm diagram
&lt;/h3&gt;

&lt;p&gt;Link algorithm diagram: &lt;a href="https://ibb.co/BVLFC1ms" rel="noopener noreferrer"&gt;Image&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flowchart above illustrates the complete subtitle-to-audio conversion algorithm with optimized memory management and streaming architecture. Here's how it works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Initialization Phase&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The process begins by downloading and parsing the SRT subtitle file from the provided URL&lt;/li&gt;
&lt;li&gt;It pre-calculates the total PCM (Pulse Code Modulation) audio size based on subtitle duration and timing information&lt;/li&gt;
&lt;li&gt;An S3 Multipart Upload is initialized to enable streaming upload without holding the entire file in memory&lt;/li&gt;
&lt;li&gt;A WAV header is created and added to the part buffer, initializing tracking variables (part_number=1, content_id index=0, current_part=b"")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Batch Processing Loop&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of processing all subtitles at once, the algorithm processes them in batches of approximately 15 subtitles&lt;/li&gt;
&lt;li&gt;For each batch, it retrieves the next set of subtitle entries to process&lt;/li&gt;
&lt;li&gt;This batching approach prevents memory overflow by keeping only a small subset of subtitles in memory at any time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Audio Generation for Each Subtitle&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each subtitle in the current batch:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Calculate speech speed&lt;/strong&gt;: Determines the appropriate speaking rate to fit the subtitle text within its designated time window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate audio using Google Cloud TTS&lt;/strong&gt;: Converts the subtitle text to audio using the selected voice settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply fade effects&lt;/strong&gt;: Adds 250ms fade-in and fade-out to prevent popping noises between audio segments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculate and add silence padding&lt;/strong&gt;: Adds appropriate silence gaps to ensure precise alignment with subtitle timing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Streaming Upload Mechanism&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As audio segments are generated, PCM data is appended to the part buffer&lt;/li&gt;
&lt;li&gt;When the buffer reaches 5MB or when processing the last batch, the current part is uploaded to S3 immediately&lt;/li&gt;
&lt;li&gt;After each upload, the buffer is cleared and the part_number is incremented&lt;/li&gt;
&lt;li&gt;This streaming approach ensures that Lambda memory usage remains constant (around 5-10MB) regardless of the total audio file size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Finalization Phase&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once all subtitle batches have been processed and all parts uploaded, the algorithm completes the S3 Multipart Upload&lt;/li&gt;
&lt;li&gt;The final audio file is assembled from all uploaded parts and becomes available on S3&lt;/li&gt;
&lt;li&gt;Error handling paths are included to manage upload failures or conversion issues (shown by the red error node)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Flow Control&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The algorithm uses two main decision loops:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"More batches to process?"&lt;/strong&gt;: Controls the outer loop for batch processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"More subtitles in batch?"&lt;/strong&gt;: Controls the inner loop for processing individual subtitles within a batch&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;"Buffer size &amp;gt;= 5MB OR last batch?"&lt;/strong&gt;: Triggers S3 part uploads at optimal intervals&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Optimizations
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Batch Processing&lt;/strong&gt;: Subtitles are processed in batches (default: 15 subtitles per batch) instead of loading all at once. This prevents memory overflow in Lambda.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;S3 Multipart Upload&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialize multipart upload at the start&lt;/li&gt;
&lt;li&gt;Upload parts incrementally when buffer reaches 5MB&lt;/li&gt;
&lt;li&gt;Each part is uploaded immediately and buffer is cleared&lt;/li&gt;
&lt;li&gt;Complete multipart upload at the end&lt;/li&gt;
&lt;li&gt;This approach ensures Lambda never holds the entire audio file in memory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streaming Architecture&lt;/strong&gt;: Audio data flows directly from TTS → batch buffer → S3 parts, with minimal memory footprint at any given time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fade Effects&lt;/strong&gt;: 250ms fade-in/out applied to each audio segment to eliminate popping noises between segments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory Management&lt;/strong&gt;: After each part upload, the buffer is reset, keeping memory usage constant regardless of total audio length.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid FFmpeg and audio processing libraries&lt;/strong&gt;: &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;We should avoid using FFmpeg (both the binary and Python wrappers like pydub, ffmpeg-python) in Lambda functions because they significantly increase deployment package size. &lt;/li&gt;
&lt;li&gt;FFmpeg binaries are typically 50-100MB, and audio processing libraries add another 20-50MB, easily exceeding Lambda's package size limits. &lt;/li&gt;
&lt;li&gt;Instead, we should work directly with WAV file format using Python's built-in &lt;code&gt;struct&lt;/code&gt; module and pure Python code to manipulate raw PCM audio bytes.&lt;/li&gt;
&lt;li&gt; This approach keeps the deployment package minimal (&amp;lt;15MB) while maintaining full control over audio processing operations like fade-in/fade-out, silence padding, and concatenation.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Thank you for following this tutorial on converting subtitles to audio using TTS technology.&lt;/li&gt;
&lt;li&gt;I hope this information can help you to implement this feature in your own projects efficiently. You could use these ideas and use any AI tools for generating code to implement it.&lt;/li&gt;
&lt;li&gt;This blog is written by my experience in real project that we need to save cost with lambda function, if you have any good ideas or suggestions please feel free to share with me!&lt;/li&gt;
&lt;li&gt;You could find more on my blogs: &lt;a href="https://leon-pham.vercel.app/blogs" rel="noopener noreferrer"&gt;Website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>lambda</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Understanding the Factory Method Pattern</title>
      <dc:creator>longtk26</dc:creator>
      <pubDate>Sat, 04 Jan 2025 15:18:34 +0000</pubDate>
      <link>https://dev.to/longtk26/understanding-the-factory-method-pattern-1ljc</link>
      <guid>https://dev.to/longtk26/understanding-the-factory-method-pattern-1ljc</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Hi everyone, I am writing this post to share my knowledge as I continue learning about design patterns. Today, I will present the Factory Method Pattern, which is a design pattern commonly used in real-world applications. If there are any mistakes in my post, please feel free to comment below, and I will gladly fix and update it.&lt;/p&gt;

&lt;p&gt;Factory method pattern provides an interface for creating objects in a superclass, but allow subclasses to alter the type of objects that will be created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;p&gt;Assume you have a bank application, and you’re building a feature for transferring money through various methods like bank transfer, paypal transfer,…&lt;/p&gt;

&lt;p&gt;Before using the Factory Method pattern, let’s examine the scenario without it.&lt;/p&gt;

&lt;p&gt;I will give an example implemented in Java.&lt;/p&gt;

&lt;p&gt;Situation: Person1 sends money to Person2 using a transfer method (Bank Transfer or PayPal Transfer).&lt;/p&gt;

&lt;p&gt;Folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;problem/
├─ BankApp.java
├─ service/
│  ├─ PaypalTransferPayment.java
│  ├─ BankTransferPayment.java
├─ data/
│  ├─ Person.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the main application, create two persons with default amounts of money.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;problem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;problem.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankApp&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jane"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;BankTransferPayment&lt;/code&gt;  and &lt;code&gt;PaypalTransferPayment&lt;/code&gt; classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;problem.service&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;problem.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankTransferPayment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deposit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bank transfer payment success."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;problem.service&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;problem.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaypalPayment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deposit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Paypal transfer payment success."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implement the logic in the main function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;problem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;problem.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;problem.service.BankTransferPayment&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;problem.service.PaypalPayment&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankApp&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jane"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"BANK_TRANSFER"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BANK_TRANSFER"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;BankTransferPayment&lt;/span&gt; &lt;span class="n"&gt;bankTransferPayment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankTransferPayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;bankTransferPayment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"===Method bank_transfer==="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" has "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" has "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PAYPAL"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;PaypalPayment&lt;/span&gt; &lt;span class="n"&gt;paypalPayment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaypalPayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;paypalPayment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"===Method paypal==="&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" has "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" has "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAmount&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problems with the current implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Repetitive code&lt;/strong&gt;: The &lt;code&gt;processPayment&lt;/code&gt; method logic is repeated for every payment method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tightly coupled code&lt;/strong&gt;: The application needs to create the payment method objects itself, making it hard to extend the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability issues&lt;/strong&gt;: If new payment methods are added, the source code becomes more complex and harder to maintain.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;The solution to the above situation is to use factory method pattern. So, how do we apply it ?&lt;/p&gt;

&lt;p&gt;In the example above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each &lt;code&gt;if-else&lt;/code&gt; block calls the &lt;code&gt;processPayment&lt;/code&gt; method, which leads to repetitive code.&lt;/li&gt;
&lt;li&gt;Objects are created based on the payment type condition, making the code messy with excessive &lt;code&gt;if-else&lt;/code&gt; statements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve these issues, the Factory Method pattern will be implemented step by step.&lt;/p&gt;

&lt;p&gt;Folder structure (solution):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;solution&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nc"&gt;BankApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="n"&gt;payments&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nc"&gt;PaymentFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nc"&gt;BankTransferPayment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nc"&gt;PaypalTransferPayment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 1: Create &lt;code&gt;Payment&lt;/code&gt; interface, declares common method  &lt;code&gt;processPayment&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Step 1: Create an interface for the payment&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Create &lt;code&gt;BankTransferPayment&lt;/code&gt; and &lt;code&gt;PaypalTransferPayment&lt;/code&gt; classes implement &lt;code&gt;Payment&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Step 2: Create a class that implements the Payment interface&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankTransferPayment&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deposit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bank transfer payment success."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaypalPayment&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fromAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withdraw&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;toAccount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deposit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Paypal transfer payment success."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Create &lt;code&gt;PaymentFactory&lt;/code&gt; class. This class is responsible for creating objects based on payment type condition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentFactory&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="nf"&gt;createPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalsIgnoreCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BANK_TRANSFER"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BankTransferPayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalsIgnoreCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PAYPAL"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PaypalPayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Use the Factory in the Main Application.&lt;/p&gt;

&lt;p&gt;Modify the main function to use the Factory Method pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;solution&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments.Payment&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments.PaymentFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankApp&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jane"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PAYPAL"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaymentFactory&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;createPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits of Using the Factory Method Pattern
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The code is &lt;strong&gt;cleaner&lt;/strong&gt; and more structured.&lt;/li&gt;
&lt;li&gt;Repetitive calls to &lt;code&gt;processPayment&lt;/code&gt; in multiple &lt;code&gt;if-else&lt;/code&gt; blocks are &lt;strong&gt;eliminated&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Object creation is &lt;strong&gt;delegated to the factory&lt;/strong&gt;, improving maintainability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Bonus&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To make the &lt;code&gt;PaymentFactory&lt;/code&gt; class comply with the Open/Closed Principle (from SOLID principles), you can implement a dynamic registration mechanism using the &lt;strong&gt;Strategy Pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Updated PaymentFactory.java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.HashMap&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Map&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentFactory&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;paymentMaps&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PaymentFactory&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;paymentMaps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="nf"&gt;createPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;paymentMaps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;registerPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;paymentMaps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentType&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;PaymentFactory&lt;/span&gt; &lt;span class="nf"&gt;initializePaymentMethods&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="n"&gt;bankTransferPayment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BankTransferPayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="n"&gt;paypalPayment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaypalPayment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;registerPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BANK_TRANSFER"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bankTransferPayment&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;registerPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PAYPAL"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paypalPayment&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the Updated Factory in the Main Application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;solution&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.data.Person&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments.Payment&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;solution.service.payments.PaymentFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BankApp&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jane"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;paymentMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"BANK_TRANSFER"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaymentFactory&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initializePaymentMethods&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentMethod&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;By applying this approach, the code adheres to the &lt;strong&gt;Open/Closed Principle&lt;/strong&gt;, enabling the addition of new payment methods without modifying the &lt;code&gt;PaymentFactory&lt;/code&gt; logic.&lt;/p&gt;

&lt;p&gt;I hope this post will be helpful to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  References:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://refactoring.guru/design-patterns/factory-method" rel="noopener noreferrer"&gt;guru-design-patterns&lt;/a&gt;&lt;/p&gt;

</description>
      <category>designpatterns</category>
      <category>java</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
