<?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: Shrestha Pandey</title>
    <description>The latest articles on DEV Community by Shrestha Pandey (@shresthapandey).</description>
    <link>https://dev.to/shresthapandey</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%2F3775845%2Fa627b42c-6d80-4c14-ba70-55b0c2cbcc08.jpg</url>
      <title>DEV Community: Shrestha Pandey</title>
      <link>https://dev.to/shresthapandey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shresthapandey"/>
    <language>en</language>
    <item>
      <title>Mojo Hits 1.0: A Technical Look</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Fri, 21 Aug 2026 11:41:21 +0000</pubDate>
      <link>https://dev.to/shresthapandey/mojo-hits-10-a-technical-look-50ga</link>
      <guid>https://dev.to/shresthapandey/mojo-hits-10-a-technical-look-50ga</guid>
      <description>&lt;p&gt;On August 11, 2026, Modular released Mojo 1.0 as part of the broader Modular 26.5 platform update. This is not a minor version bump dressed up with marketing language. It closes a three-year period during which the language's syntax, standard library, and core semantics changed release over release, often breaking source compatibility for anyone maintaining a nontrivial codebase on top of it. This article examines what changed at the language level, what the stability guarantee actually covers, how the memory model and GPU targeting evolved, and where the language still has real gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 1.0 is a governance change
&lt;/h2&gt;

&lt;p&gt;Since Modular open-sourced the Mojo standard library in 2024, the project has taken in roughly 1,100 pull requests from close to 200 external contributors, touching more than 200,000 lines of code, with well over a thousand additional issues filed by the community. That volume of contribution is a healthy sign for an open-source project, but it also explains why pre-1.0 Mojo was difficult to build durable software on: Modular was using the language internally to build its own commercial infrastructure — the MAX inference framework and Modular Cloud — and the pace of internal iteration routinely outstripped what downstream projects could track.&lt;/p&gt;

&lt;p&gt;The 1.0 stability policy borrows its model from mature systems languages, C++ being the explicit reference point. Within the 1.x line, changes are expected to be additive by default. Breaking changes remain possible, but Modular has committed to handling them the way a mature toolchain does: deliberately, with migration paths, rather than as routine release noise.&lt;/p&gt;

&lt;p&gt;Importantly, "stable" in Mojo 1.0 does not mean "the entire standard library is frozen." Modular introduced a formal stabilization marker system with this release, and only a deliberately small initial set of APIs carries the full stability guarantee. Traits such as &lt;code&gt;Deinitable&lt;/code&gt;, &lt;code&gt;Movable&lt;/code&gt;, &lt;code&gt;Copyable&lt;/code&gt;, and &lt;code&gt;ImplicitlyCopyable&lt;/code&gt; are fully stable as of 1.0. Widely used types like &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;List&lt;/code&gt;, &lt;code&gt;Span&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Bool&lt;/code&gt;, and &lt;code&gt;Optional&lt;/code&gt; have only some of their APIs marked stable so far — the rest remains subject to change in later 1.x releases. Developers building long-lived systems on Mojo need to check the stabilization marker on each API surface they depend on, not just the language version number.&lt;/p&gt;

&lt;p&gt;There's a second, easily missed caveat: the stability guarantee currently covers source compatibility, not ABI compatibility. Binary compatibility across compiler versions is not yet promised, which matters if you're distributing precompiled Mojo libraries rather than recompiling from source on each release.&lt;/p&gt;

&lt;p&gt;Because so much surface area was locked down in this release, 1.0 actually ships with more breaking changes than a typical Mojo release — the tradeoff Modular made deliberately to get names, defaults, and safety boundaries right before freezing them. Nearly every one of those breaking changes ships with a deprecated alias and an automated compiler fix-it, so most migrations are mechanical rather than requiring a manual audit of every call site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language-level changes worth knowing before you port code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Declaration and closure unification
&lt;/h3&gt;

&lt;p&gt;Mojo has converged on a single way to declare a mutable binding. Where earlier versions allowed implicit declaration in some contexts — convenient, but a source of "typo silently becomes a new variable" bugs — 1.0 consistently requires &lt;code&gt;var&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;fn compute_mean(data: List[Float64]) -&amp;gt; Float64:
    var total: Float64 = 0.0
    var count = 0
    for value in data:
        total += value
        count += 1
    return total / count
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Closures went through a parallel unification. The old &lt;code&gt;unified&lt;/code&gt; keyword is gone. Capture semantics are now expressed with an explicit capture list &lt;code&gt;{...}&lt;/code&gt; following the function signature; an empty &lt;code&gt;{}&lt;/code&gt; denotes a unified closure with no captures, while omitting the capture list entirely marks a closure as legacy. Stateless closures now auto-lift to top-level functions and can be passed directly as FFI callbacks. A new &lt;code&gt;thin&lt;/code&gt; function-pointer effect exists specifically for declaring a plain function pointer type that carries no captured state at all — useful when you're handing a callback to a C API that expects a bare function pointer.&lt;/p&gt;

&lt;p&gt;Mojo 1.0 also adds real single-expression lambda syntax, closing a long-standing ergonomic gap for anyone translating Python code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var doubled = [x * 2 for x in values]
var by_length = sorted(words, key=lambda w: len(w))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, a lambda desugars to a nested &lt;code&gt;def&lt;/code&gt;, so it's syntactic sugar rather than a distinct closure mechanism — but it removes the friction of writing a named nested function for every trivial callback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointer unification and non-nullability by default
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Pointer&lt;/code&gt; and &lt;code&gt;UnsafePointer&lt;/code&gt; — previously two separate types with overlapping responsibilities — are now a single &lt;code&gt;Pointer&lt;/code&gt; type. The change in philosophy is more significant than the rename: instead of marking an entire type as "unsafe," individual operations on a pointer are now marked unsafe at the call site. This gives the compiler and human reviewers a much more granular signal about where actual unsafety occurs in a codebase, rather than treating every use of a pointer type as equally risky.&lt;/p&gt;

&lt;p&gt;The unification also removed pointer nullability as a default. The old pattern of a default-constructed null pointer is deprecated; &lt;code&gt;Pointer&lt;/code&gt; no longer conforms to &lt;code&gt;Defaultable&lt;/code&gt; or &lt;code&gt;Boolable&lt;/code&gt; for this purpose. If a pointer genuinely needs to represent "no value," you now wrap it explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var maybe_ptr: Optional[Pointer[Int]] = None

if maybe_ptr:
    print(maybe_ptr.value()[])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Optional[Pointer[T]]&lt;/code&gt; reuses the null address as the &lt;code&gt;None&lt;/code&gt; niche internally, so this wrapping costs nothing at runtime and remains layout-compatible with FFI code expecting a raw nullable pointer. &lt;code&gt;UnsafeAnyOrigin&lt;/code&gt;, the escape hatch used to widen a reference's lifetime arbitrarily, is also harder to reach by accident now: implicit widening to it is deprecated, and a struct field can no longer silently hide one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Collection semantics: bounds checking and the loss of negative indexing
&lt;/h3&gt;

&lt;p&gt;Standard library collections are bounds-checked by default in 1.0, and — more disruptively for anyone porting Python — negative indexing has been removed entirely. &lt;code&gt;x[-1]&lt;/code&gt; is now a compile-time error rather than "last element," and the idiomatic replacement is &lt;code&gt;x[len(x) - 1]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a real source of breakage for Python-to-Mojo ports, and it's worth grepping for &lt;code&gt;[-1]&lt;/code&gt; and &lt;code&gt;[-N]&lt;/code&gt; patterns specifically before assuming a migrated module compiles cleanly. The rationale is consistent with Mojo's broader safety posture: implicit wraparound indexing is a common source of subtle bugs in array-heavy code, and the language would rather force an explicit expression than silently do something Python-programmer-intuitive but easy to get wrong at the boundaries.&lt;/p&gt;

&lt;p&gt;A related but separate change: list literals like &lt;code&gt;[1, 2, 3]&lt;/code&gt; now construct an &lt;code&gt;Array&lt;/code&gt; by default rather than a &lt;code&gt;List&lt;/code&gt;. &lt;code&gt;Array&lt;/code&gt; is a fixed-size, stack-friendly container, while &lt;code&gt;List&lt;/code&gt; remains the growable heap-backed type — so code that relied on list-literal syntax producing a resizable container needs to switch to an explicit &lt;code&gt;List(...)&lt;/code&gt; constructor call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference invalidation diagnostics and interior origins
&lt;/h3&gt;

&lt;p&gt;The most consequential correctness feature in this release is compile-time detection of reference invalidation. Mojo's existing origin/lifetime checker already prevented references from outliving the value they point to; 1.0 extends that checking to catch a narrower and nastier class of bug — a reference into a container becoming invalid because a mutation on the same container reallocated its backing storage. The canonical example is holding a reference to an element of a &lt;code&gt;List&lt;/code&gt; and then calling &lt;code&gt;.append()&lt;/code&gt; on that same list in a way that could trigger a reallocation. Previously this was a silent dangling reference; the compiler now rejects it statically.&lt;/p&gt;

&lt;p&gt;This is supported by an experimental capability called interior origins, which lets &lt;code&gt;List&lt;/code&gt;, &lt;code&gt;Dict&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, and a handful of other standard library types return element references whose origin is explicitly tied to the interior of the container, rather than treating the whole container as one undifferentiated origin. That distinction is what lets the checker reason about "this reference came from inside this specific container" instead of being forced to either over-approximate (reject too much valid code) or under-approximate (miss real bugs).&lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller but real breaking changes
&lt;/h3&gt;

&lt;p&gt;A number of narrower changes are easy to miss in a changelog skim but will surface immediately if your code touches them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;where&lt;/code&gt; clauses can now carry an optional string-literal diagnostic message — &lt;code&gt;where(condition, "message")&lt;/code&gt; — which the compiler surfaces when the constraint fails, making generic code failures far more actionable than a bare constraint-violation error.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;==&lt;/code&gt; and &lt;code&gt;!=&lt;/code&gt; now work for type equality checks directly.&lt;/li&gt;
&lt;li&gt;Method &lt;code&gt;self&lt;/code&gt; parameters must now have type &lt;code&gt;Self&lt;/code&gt;; code that gave &lt;code&gt;self&lt;/code&gt; a different declared type needs to move that logic into a &lt;code&gt;where&lt;/code&gt; clause instead.&lt;/li&gt;
&lt;li&gt;Overloads that differ only in argument convention (&lt;code&gt;imm&lt;/code&gt; versus &lt;code&gt;mut&lt;/code&gt;) are now rejected, since the compiler cannot resolve overload selection based on convention alone.&lt;/li&gt;
&lt;li&gt;Reserved words (&lt;code&gt;class&lt;/code&gt;, &lt;code&gt;del&lt;/code&gt;, &lt;code&gt;match&lt;/code&gt;, &lt;code&gt;yield&lt;/code&gt;, and similar) can no longer be used as free function names. This previously produced a function that could never actually be called; it's now a declaration-time error.&lt;/li&gt;
&lt;li&gt;The compiler tightened whitespace rules in specific spots — no newline is permitted between &lt;code&gt;def&lt;/code&gt;/&lt;code&gt;struct&lt;/code&gt;/&lt;code&gt;trait&lt;/code&gt;/&lt;code&gt;comptime&lt;/code&gt; and the following identifier, between &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;def&lt;/code&gt;, or in the middle of an unparenthesized import statement.&lt;/li&gt;
&lt;li&gt;Keyword variadics can now be forwarded from one function to another using Python-style &lt;code&gt;**&lt;/code&gt; syntax, closing a gap that made wrapping functions with many optional keyword arguments awkward.
Individually these are small. Collectively, they're why Modular flagged this release as carrying more breaking changes than usual, and why the deprecated-alias-plus-fix-it approach matters: without it, adopting 1.0 on an existing several-thousand-line codebase would be a multi-day manual audit rather than a mostly-automated pass.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GPU and accelerator targeting
&lt;/h2&gt;

&lt;p&gt;Mojo's differentiator has never really been "Python syntax" on its own — it's that the language compiles through MLIR rather than directly through LLVM. LLVM targets one hardware architecture at a time; MLIR is designed to let multiple levels of abstraction coexist in a single compilation pipeline, which is what allows the same Mojo source to be specialized for CPU, GPU, and other accelerator targets without hand-written per-vendor code paths. Modular has built a kernel-generation layer, internally referred to as KGEN, on top of MLIR specifically to represent parametric AI kernels before they're instantiated for a given hardware target. In practice, this is what lets a Mojo kernel target NVIDIA Tensor Cores, AMD matrix accelerators, and other accelerator hardware from one source file.&lt;/p&gt;

&lt;p&gt;1.0 also clarifies the rules at the CPU/GPU boundary. &lt;code&gt;Int&lt;/code&gt; and &lt;code&gt;UInt&lt;/code&gt; use the host's native word size, which isn't guaranteed to match the device's — so when a value of type &lt;code&gt;Int&lt;/code&gt; or &lt;code&gt;UInt&lt;/code&gt; crosses into a GPU kernel, Mojo remaps it to the corresponding fixed-width type rather than leaving the width ambiguous. For code where the exact bit width matters at the register or memory-layout level — file formats, pixel buffers, hardware registers — the standard library guidance is still to reach for an explicit sized type yourself rather than relying on the remap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn kernel(n: Int32): ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some accelerator-specific APIs also moved out of core Mojo entirely and into a separate &lt;code&gt;max&lt;/code&gt; package, with the &lt;code&gt;layout&lt;/code&gt; module now living on the MAX side rather than in the language proper. This reflects a deliberate architectural split: Mojo is positioning itself as a general-purpose systems language, with MAX as the layer responsible for tensor-aware, kernel-aware, inference-serving concerns.&lt;/p&gt;

&lt;p&gt;On the performance side, independent validation is available from a 2025 study by researchers at Oak Ridge National Laboratory, presented at the SC25 WACCPD workshop, where it received the Best Paper award. The study benchmarked Mojo GPU kernels against CUDA on an NVIDIA H100 and against HIP on an AMD MI300A, using real HPC science workloads rather than synthetic microbenchmarks. For memory-bound workloads — a stencil computation was the representative case — Mojo averaged approximately 87% of CUDA's throughput on the H100 in both single and double precision, with a somewhat larger gap at double precision. On the AMD MI300A, Mojo was broadly competitive for memory-bound work but showed a more pronounced gap for atomic operations and fast-math-heavy compute-bound workloads. The study's authors framed the result as evidence that Mojo's write-once, cross-vendor portability comes at a modest and workload-dependent cost relative to hand-tuned, vendor-specific code — notable given that the benchmarks were run against a pre-1.0 version of the language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python interoperability
&lt;/h2&gt;

&lt;p&gt;Mojo is frequently described as "a superset of Python," but that framing has been explicitly walked back by Modular over the past year; the language is not source-compatible with Python 3 and does not aim to be. Mojo uses struct types with compile-time-determined layout rather than Python's dynamic class system, and it interoperates with Python code through the CPython runtime rather than by directly executing Python source. You cannot rename a &lt;code&gt;.py&lt;/code&gt; file to &lt;code&gt;.mojo&lt;/code&gt; and expect it to compile — the practical adoption model is writing new performance-critical code in Mojo while continuing to call into the existing Python ecosystem across a runtime bridge.&lt;/p&gt;

&lt;p&gt;That bridge got measurably faster in this release. Arithmetic, comparison, and containment operations on &lt;code&gt;PythonObject&lt;/code&gt; now go directly through CPython's abstract object protocols instead of a slower dispatch path, and Modular's own measurements show roughly a 12x improvement for call-boundary-heavy patterns like repeated &lt;code&gt;a + b&lt;/code&gt; or &lt;code&gt;a &amp;lt; b&lt;/code&gt; comparisons. It's worth being precise about what this number means: it is not a claim that Mojo code is 12x faster than equivalent Python — it's specifically the overhead of crossing the Mojo/CPython boundary shrinking for arithmetic-heavy interop patterns. For code with a hot loop that repeatedly touches Python objects from Mojo, this is a legitimate and measurable win; for code that stays entirely within Mojo-native types, it's not directly relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open source status
&lt;/h2&gt;

&lt;p&gt;The Mojo standard library has been available under the Apache 2.0 license (with LLVM exceptions) since 2024. The compiler and toolchain were the remaining proprietary piece, and Modular had publicly committed to open-sourcing them by the end of 2026. That commitment was fulfilled within the past week: following ModCon, Modular's annual developer conference held August 18 in San Francisco, the compiler and toolchain were released under Apache 2.0 as well, closing the loop on a promise the company had made since Mojo's original 2023 launch.&lt;/p&gt;

&lt;p&gt;This detail matters beyond ideology. Modular's acquisition by Qualcomm closed on July 28, 2026. Mojo's core value proposition to the AI infrastructure market has always rested on vendor neutrality — the claim that a Mojo kernel targeting NVIDIA hardware and one targeting AMD hardware get equally serious compiler treatment. That claim is harder to simply trust once the compiler is owned by a company that also designs its own accelerator silicon. An open-source compiler doesn't eliminate that concern, but it does convert an unverifiable promise into one the community can audit directly by inspecting how code generation actually treats each hardware backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's still missing
&lt;/h2&gt;

&lt;p&gt;Mojo 1.0 is explicitly not a claim that the language is feature-complete. Three capabilities called out on Modular's own roadmap remain absent: a mature asynchronous programming model (&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; exists in a limited form, but a full async runtime story is still forthcoming), pattern matching, and union types. Teams whose workloads are concurrency-heavy rather than compute-heavy — network services doing a lot of concurrent I/O, for instance — will feel these gaps directly; Mojo's current strengths are firmly on the CPU/GPU-bound compute side of the spectrum, not on the async-service side.&lt;/p&gt;

&lt;p&gt;The library ecosystem is real but still young relative to Python's or Rust's. Community-maintained projects exist and are actively developed — an HTTP framework called Lightbug, a pure-Mojo JSON library called EmberJSON, and a type-safe dimensional-analysis library called Kelvin are three commonly cited examples — but developers evaluating Mojo for a given task should expect to write more of their own supporting infrastructure than they would in a decade-old ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical guidance for adoption
&lt;/h2&gt;

&lt;p&gt;Upgrading is a one-line operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; mojo
uv pip &lt;span class="nb"&gt;install &lt;/span&gt;max[all]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before migrating an existing codebase, it's worth budgeting specific time for three mechanical sweeps rather than assuming the compiler's deprecated-alias fix-its catch everything silently: a search for negative indexing patterns (&lt;code&gt;[-1]&lt;/code&gt;, &lt;code&gt;[-2]&lt;/code&gt;, etc.), a check of any list-literal usage that assumed a growable &lt;code&gt;List&lt;/code&gt; rather than a fixed-size &lt;code&gt;Array&lt;/code&gt;, and a review of pointer-handling code that relied on default-null construction or implicit &lt;code&gt;Boolable&lt;/code&gt; checks on &lt;code&gt;Pointer&lt;/code&gt;/&lt;code&gt;UnsafePointer&lt;/code&gt;. None of these are large individually, but they're the changes most likely to produce a compile error that isn't automatically resolved by the compiler's suggested fix.&lt;/p&gt;

&lt;p&gt;For teams evaluating whether to adopt Mojo now versus waiting: the 1.0 stability guarantee is real for APIs explicitly marked stable, and the combination of MLIR-based cross-vendor GPU targeting with an open-source compiler is a genuinely distinctive position in the current AI infrastructure stack. The clearest fit today is numeric or tensor-heavy code that needs to target multiple accelerator vendors without maintaining separate CUDA and ROCm code paths, or performance-critical inner loops embedded in an otherwise Python-based system, where a full rewrite into C++ or Rust would be disproportionate to the problem. Teams that need async-heavy concurrency, pattern matching, or a deep third-party package ecosystem comparable to PyPI or crates.io should treat those as open gaps rather than assumptions, and plan accordingly.&lt;/p&gt;

&lt;p&gt;For more such in-depth developer content, visit:&lt;br&gt;
&lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mojo</category>
      <category>webdev</category>
      <category>programming</category>
      <category>vickybytes</category>
    </item>
    <item>
      <title>A Complete Guide to GitHub Stacked PRs</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Wed, 12 Aug 2026 12:36:46 +0000</pubDate>
      <link>https://dev.to/shresthapandey/a-complete-guide-to-github-stacked-prs-49oa</link>
      <guid>https://dev.to/shresthapandey/a-complete-guide-to-github-stacked-prs-49oa</guid>
      <description>&lt;p&gt;You know that feeling when you spend three days building a feature. You open a pull request and it's 1,200 lines long. Your teammate sees it, and says, "I'll review this later." That later becomes tomorrow. Tomorrow becomes next week. Meanwhile, main moves on, conflicts appear, and your "small feature" turns into a mini-project.&lt;/p&gt;

&lt;p&gt;There's a better way, and it's called stacked PRs. GitHub now supports it natively, in &lt;strong&gt;public preview as of July 30, 2026&lt;/strong&gt;, rolling out to all repositories. This article explains it in plain language.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Current status:&lt;/strong&gt; Stacked PRs are in public preview. The core feature (creating stacks, reviewing each layer, merging in one click) works today. Merge queue support is rolling out separately over the following weeks, so if your repo relies on a merge queue, double-check compatibility before betting a workflow on it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem: One Giant PR That No One Wants to Review
&lt;/h2&gt;

&lt;p&gt;Imagine you're adding a new feature: "User preferences."&lt;/p&gt;

&lt;p&gt;To make it work, you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a new database table&lt;/li&gt;
&lt;li&gt;Create API endpoints&lt;/li&gt;
&lt;li&gt;Build a settings page in the UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you do all of this in one branch and open one PR, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A huge diff that's hard to understand&lt;/li&gt;
&lt;li&gt;Reviewers who don't know where to start&lt;/li&gt;
&lt;li&gt;Long wait times and painful merge conflicts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the "monster PR" problem, which slows everyone down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea: Break the Feature Into Layers
&lt;/h2&gt;

&lt;p&gt;Instead of one giant change, think in layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Layer 1 – Database:&lt;/strong&gt; Add the &lt;code&gt;user_preferences&lt;/code&gt; table and migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 2 – API:&lt;/strong&gt; Add endpoints to read and update preferences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer 3 – UI:&lt;/strong&gt; Add the settings page that calls those endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each layer is small, focused, and easy to review. You still build the full feature, but you split the work into three smaller pull requests that build on top of each other. That's a stack.&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%2F53h9fo0iej0yaj36wgsi.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%2F53h9fo0iej0yaj36wgsi.png" alt="Normal vs Stacked Workflow" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each PR's base is the branch below it, not main. GitHub shows this as a &lt;strong&gt;stack map&lt;/strong&gt; at the top of the pull request, so reviewers can see how the change they're looking at fits into the larger work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Worth It
&lt;/h2&gt;

&lt;p&gt;Stacked PRs solve real problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster reviews:&lt;/strong&gt; A 200-line PR gets reviewed much faster than a 1,200-line PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less pain when merging:&lt;/strong&gt; Smaller PRs mean fewer conflicts and easier rebases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can ship incrementally:&lt;/strong&gt; Once the DB PR is approved, you can merge it while still working on the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub now supports it natively:&lt;/strong&gt; As of July 30, 2026 this is built into the pull request workflow itself — no third-party tooling required to get the basic experience.
If your team complains about slow reviews or giant PRs, this is a practical fix.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step-by-Step: Your First Stacked PR
&lt;/h2&gt;

&lt;p&gt;Let's walk through a real example. We'll use the "User preferences" feature with three layers: DB, API, UI.&lt;/p&gt;

&lt;p&gt;You only need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git installed&lt;/li&gt;
&lt;li&gt;GitHub CLI (&lt;code&gt;gh&lt;/code&gt;) installed&lt;/li&gt;
&lt;li&gt;A repo with stacked PRs enabled (it's rolling out progressively, so check it's available on yours)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Install the gh-stack extension
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh extension &lt;span class="nb"&gt;install &lt;/span&gt;github/gh-stack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check it's installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh stack &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see help text, you're good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Start From main
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout main
git pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Create the First Layer (Database)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh stack init feat/prefs-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new branch &lt;code&gt;feat/prefs-db&lt;/code&gt; from &lt;code&gt;main&lt;/code&gt; and starts a new stack.&lt;/p&gt;

&lt;p&gt;Now make your database changes: add the migration file, update models, run tests. Then commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add user_preferences table and migration"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you have one branch with one logical change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add the Second Layer (API)
&lt;/h3&gt;

&lt;p&gt;From the DB branch, add the next layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh stack add feat/prefs-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates &lt;code&gt;feat/prefs-api&lt;/code&gt; on top of &lt;code&gt;feat/prefs-db&lt;/code&gt; and keeps the stack structure.&lt;/p&gt;

&lt;p&gt;Implement the API: add controller/routes, &lt;code&gt;GET /preferences&lt;/code&gt; and &lt;code&gt;PUT /preferences&lt;/code&gt;, add tests.&lt;/p&gt;

&lt;p&gt;Commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add preferences API endpoints"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have two layers: DB → API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Add the Third Layer (UI)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh stack add feat/prefs-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're now on &lt;code&gt;feat/prefs-ui&lt;/code&gt;, sitting on &lt;code&gt;feat/prefs-api&lt;/code&gt;, sitting on &lt;code&gt;feat/prefs-db&lt;/code&gt;, sitting on &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Implement the UI: settings page component, connect to the API, handle loading and error states. &lt;/p&gt;

&lt;p&gt;Commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add user preferences UI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have three layers ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Push the Whole Stack to GitHub
&lt;/h3&gt;

&lt;p&gt;From the top branch (&lt;code&gt;feat/prefs-ui&lt;/code&gt;), run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh stack submit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command pushes all three branches in the right order and creates three pull requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PR1: &lt;code&gt;feat/prefs-db&lt;/code&gt; → &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PR2: &lt;code&gt;feat/prefs-api&lt;/code&gt; → &lt;code&gt;feat/prefs-db&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PR3: &lt;code&gt;feat/prefs-ui&lt;/code&gt; → &lt;code&gt;feat/prefs-api&lt;/code&gt;
It links them together as a stack in the GitHub UI. Open any of these PRs and you'll see a stack map showing "this PR is part of a stack" with links to the PRs above and below. Reviewers see the full story, but each PR shows only its own changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Reviewers See It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Without stacked PRs:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;one PR, 1,200 lines, touching DB, API, and UI. The reviewer doesn't know where to start and leaves a vague comment: "This is huge, can we split it?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With stacked PRs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PR1 (DB): 150 lines, just schema and migration. "Is the table correct? Is the migration safe?"&lt;/li&gt;
&lt;li&gt;PR2 (API): 200 lines, just endpoints. "Are routes correct? Auth in place? Tests passing?"&lt;/li&gt;
&lt;li&gt;PR3 (UI): 250 lines, just frontend. "Does the UI match the design? Error handling okay?"
Each PR is focused. Each review takes 10–15 minutes instead of an hour. That's the whole point.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Happens When You Need to Change Something?
&lt;/h2&gt;

&lt;p&gt;Say the reviewer asks you to add an index to the &lt;code&gt;user_preferences&lt;/code&gt; table (DB layer), plus one more API validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix the DB branch:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feat/prefs-db
&lt;span class="c"&gt;# edit migration, add index&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add index on user_id"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rebase the layers above.&lt;/strong&gt; Rather than rebasing each branch by hand, use the CLI's built-in cascade rebase, which rebases every branch in the stack onto its updated parent in one step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh stack rebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a conflict comes up, &lt;code&gt;gh stack rebase&lt;/code&gt; walks you through resolving it branch by branch and restores everything to a clean state if you abort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update the stack on GitHub:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh stack push
gh stack submit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;gh stack push&lt;/code&gt; pushes the rebased branches (force-with-lease where needed), and &lt;code&gt;gh stack submit&lt;/code&gt; updates the existing PRs and keeps the stack links intact. Reviewers see your new commits in the right PRs.&lt;/p&gt;

&lt;p&gt;Yes, there's rebasing. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're rebasing small, focused branches (easier than one giant branch)&lt;/li&gt;
&lt;li&gt;You do this often, so it becomes routine&lt;/li&gt;
&lt;li&gt;The CLI's cascade rebase handles the heavy lifting instead of you doing it branch-by-branch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Merging: You Don't Have to Wait for Everything to Be Done
&lt;/h2&gt;

&lt;p&gt;One of the best parts: you can merge layer by layer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PR1 (DB) is approved and CI is green → merge it.&lt;/li&gt;
&lt;li&gt;PR2 (API) and PR3 (UI) stay open while you keep working.&lt;/li&gt;
&lt;li&gt;After PR1 merges, the branches above it automatically rebase and retarget onto the updated &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Later, PR2 gets approved → merge. Then PR3 → merge.
By default, merging the topmost ready PR in a stack lands that PR &lt;em&gt;and&lt;/em&gt; every unmerged layer below it in a single operation — you don't need to enable anything special for this. Your existing branch protections and required checks still govern what actually reaches &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Example: From "Monster PR" to Three Small PRs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before: One Big PR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Branch: &lt;code&gt;feat/user-preferences&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migration + models: ~200 lines&lt;/li&gt;
&lt;li&gt;API endpoints + tests: ~350 lines&lt;/li&gt;
&lt;li&gt;UI components + styles + tests: ~650 lines
Total: ~1,200 lines in one PR. The reviewer doesn't know where to start, requests changes on tangled code, and keeps pushing the review off. The PR sits for days, conflicts appear, morale drops.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After: Three Stacked PRs&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PR1 – DB&lt;/strong&gt; (&lt;code&gt;feat/prefs-db&lt;/code&gt;): migration + basic model + validation tests. ~150 lines. Focus: "Is the schema right? Is the migration safe?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PR2 – API&lt;/strong&gt; (&lt;code&gt;feat/prefs-api&lt;/code&gt;): controller + routes, auth checks, input validation, API tests. ~250 lines. Focus: "Are endpoints correct and secure?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PR3 – UI&lt;/strong&gt; (&lt;code&gt;feat/prefs-ui&lt;/code&gt;): settings page, API integration, loading/error states, E2E tests. ~300 lines. Focus: "Does the UX match the spec?"
Each reviewer sees a clear, focused job. Reviews happen faster. You get feedback sooner and merge sooner.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Questions (Answered Simply)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Isn't this more work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Upfront, yes — more branches, more PRs. But each PR is easier to create (smaller diff, clearer description), each review is faster, and you spend less time resolving massive merge conflicts later. For anything beyond a tiny change, stacked PRs usually save time overall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about CI? Won't tests fail on higher PRs?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each PR runs CI against its base branch: PR1 (DB) runs on &lt;code&gt;main&lt;/code&gt;, PR2 (API) runs on &lt;code&gt;feat/prefs-db&lt;/code&gt;, PR3 (UI) runs on &lt;code&gt;feat/prefs-api&lt;/code&gt;. Some checks might only fully pass once lower PRs are merged — that's expected. Make sure required checks are green on each PR's base, and note in your PR template that some checks depend on lower layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When should I not use stacked PRs?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avoid them when the change is tiny (a single 100-line PR is fine), when the layers are too tightly coupled to review separately, or when your team is still getting comfortable with basic Git branching and rebasing.&lt;/p&gt;

&lt;p&gt;Use them when the feature naturally splits into layers (DB/API/UI, core/extension/integration), reviews are slow because PRs are too big, and your team is comfortable with rebase-based workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistakes People Make (and How to Avoid Them)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Too many layers.&lt;/strong&gt; Don't turn a small feature into 10 tiny PRs. 2–5 PRs per feature is usually right; if you need more, the feature itself may be too big.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unclear descriptions.&lt;/strong&gt; Reviewers shouldn't have to guess what a PR does or where it sits in the stack. Say what it does, what it depends on, and what follows it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignoring the bottom PR.&lt;/strong&gt; The bottom PR is the foundation — if it's messy, everything above it is shaky. Keep it clean, well-tested, and understandable on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Being afraid of rebase.&lt;/strong&gt; You'll rebase more, but each rebase is smaller, you get good at it fast, and &lt;code&gt;gh stack rebase&lt;/code&gt; reduces the manual work significantly.
## A Simple Checklist Before You Push&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before you run &lt;code&gt;gh stack submit&lt;/code&gt;, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does each PR have one clear purpose?&lt;/li&gt;
&lt;li&gt;Can someone understand the bottom PR without seeing the others?&lt;/li&gt;
&lt;li&gt;Are branch names clear (e.g., &lt;code&gt;prefs-db&lt;/code&gt;, &lt;code&gt;prefs-api&lt;/code&gt;, &lt;code&gt;prefs-ui&lt;/code&gt;)?&lt;/li&gt;
&lt;li&gt;Did you mention the stack in each PR description?&lt;/li&gt;
&lt;li&gt;Are tests passing locally on each layer?&lt;/li&gt;
&lt;li&gt;If your repo uses a merge queue, have you confirmed stacked-PR support is available yet?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If yes, you're ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR (For Your Next Feature)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Don't send one giant PR.&lt;/li&gt;
&lt;li&gt;Split your feature into 2–4 logical layers.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;gh stack&lt;/code&gt; (native to GitHub as of the July 2026 public preview) to create a stack of PRs.&lt;/li&gt;
&lt;li&gt;Let reviewers focus on small, clear diffs.&lt;/li&gt;
&lt;li&gt;Merge layer by layer, reduce conflicts, and ship faster.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>stackedprs</category>
      <category>webdev</category>
      <category>vickybytes</category>
    </item>
    <item>
      <title>How MCP Works And Why Agents Use It</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Sun, 09 Aug 2026 06:17:28 +0000</pubDate>
      <link>https://dev.to/shresthapandey/how-mcp-works-and-why-agents-use-it-32a8</link>
      <guid>https://dev.to/shresthapandey/how-mcp-works-and-why-agents-use-it-32a8</guid>
      <description>&lt;p&gt;Every few years, a problem gets solved, and the whole industry moves faster. USB-C did this for chargers. REST did this for web APIs. In late 2024, Anthropic built something similar for AI. It's called the Model Context Protocol, or MCP.&lt;/p&gt;

&lt;p&gt;The problem was, an AI model needs to use outside tools like your database, your email, your project tracker, but every tool is different, every AI app is different. Without a shared standard, someone has to build a custom bridge for every single pair.&lt;/p&gt;

&lt;p&gt;MCP is that shared standard. This article explains how it works, piece by piece. We'll also cover the newest version of MCP, called 2026-07-28. It came out just a few days ago, and it changed a lot of things. So if you read about MCP before, some of it is now out of date. This article covers both the old way and the new way.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The problem MCP solves
&lt;/h2&gt;

&lt;p&gt;Before MCP, connecting an AI to a tool meant writing custom code. That code had to talk to the tool's API. It also had to match whatever format the AI model needed. If you switched to a different AI model, you often had to rewrite that code.&lt;/p&gt;

&lt;p&gt;Let's say you have &lt;code&gt;N&lt;/code&gt; different AI apps. Maybe that's Claude Desktop, a coding assistant, and a Slack bot. And you have &lt;code&gt;M&lt;/code&gt; different tools they need to use. Maybe that's GitHub, a database, and Jira. Without a shared standard, you need custom code for every single pairing. That's &lt;code&gt;N × M&lt;/code&gt; pieces of code. It grows fast.&lt;/p&gt;

&lt;p&gt;MCP fixes this. Each tool only needs to build &lt;strong&gt;one&lt;/strong&gt; MCP server. Each AI app only needs to build &lt;strong&gt;one&lt;/strong&gt; MCP client. Now any app can talk to any tool, because they all speak the same protocol. That turns &lt;code&gt;N × M&lt;/code&gt; pieces of code into just &lt;code&gt;N + M&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Code editors solved the same kind of problem years ago with something called LSP (Language Server Protocol). MCP borrowed that same idea and built on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What MCP is
&lt;/h2&gt;

&lt;p&gt;MCP is not an app. It's not a tool you install and run. It's a set of rules for how messages get sent back and forth. Under the hood, it uses a simple message format called JSON-RPC. On top of that, MCP adds a few building blocks made just for AI: tools, resources, and prompts. We'll explain those soon.&lt;/p&gt;

&lt;p&gt;There are always three parts involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Host&lt;/strong&gt; — the app the person is actually using. This could be Claude Desktop, a coding tool, or your own app. The host holds the AI model and the conversation. It also decides what is allowed to happen. If a tool wants to do something risky, the host is what can stop it or ask for approval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt; — a small connector that lives inside the host. There's one client for every server it talks to. If the host connects to five different tools, it runs five clients. The client's job is simple: pass messages back and forth. It doesn't make decisions on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server&lt;/strong&gt; — the thing that actually offers tools or data. Most servers are simple wrappers around something that already exists, like an API or a database. The server doesn't know or care which AI model is using it.&lt;/li&gt;
&lt;/ul&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%2Foq021vmi902bdfyto5xv.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%2Foq021vmi902bdfyto5xv.png" alt="Overview of how AI models, hosts, MCP clients, and servers communicate." width="799" height="671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key point is, the AI model never talks to the server directly. The host always sits in the middle. When a tool sends back a result, the host is the one that adds it to the conversation. This matters a lot. It means the host can check things, ask the person for approval, or block something risky before it happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. How the messages actually travel
&lt;/h2&gt;

&lt;p&gt;MCP supports two main ways to send messages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;stdio&lt;/strong&gt; — the server runs as a small program right on your computer. Messages are just plain text, sent back and forth. This is the easiest option, and it's what most "install this on your laptop" guides use. There's no network involved, so it's simple and safe by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streamable HTTP&lt;/strong&gt; — this is used when the server lives somewhere else, like a company's cloud server. The client sends a request over the internet, and the server sends back a reply. Sometimes the reply comes all at once. Sometimes it streams back a little at a time. This is the option used for tools that many people share, not just one person on one laptop.&lt;/p&gt;

&lt;p&gt;Starting with the newest version of MCP, every HTTP message also includes two extra pieces of information in its header: which method is being called, and which tool it's for. It means a company's security system can check and control requests just by reading these two header values. It doesn't need to open up and read the entire message first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/mcp&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example-mcp.com&lt;/span&gt;
&lt;span class="na"&gt;MCP-Protocol-Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-07-28&lt;/span&gt;
&lt;span class="na"&gt;Mcp-Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tools/call&lt;/span&gt;
&lt;span class="na"&gt;Mcp-Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create_issue&lt;/span&gt;
&lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer eyJhbGciOi...&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"tools/call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"create_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Bug: nav bar overflow"&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;
  
  
  4. The "handshake" that used to be required
&lt;/h2&gt;

&lt;p&gt;If you read an older MCP guide, it starts with something called a handshake. The client says hello first, and tells the server what it can do. The server replies, and says what it can do. Then the client says "okay, we're ready." The server would also give the client a special ID number, and the client had to include that ID on every single message after that, so the server would remember who it was talking to.&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%2Fmhdf8mmk6vfs9brrwrog.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%2Fmhdf8mmk6vfs9brrwrog.png" alt="Shows session-based communication between a client and server." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This worked fine when the server was just a small program on your own computer, but it caused real problems for servers running in the cloud, shared by lots of people. The server had to remember which person was "who," and every request from that person had to be routed back to the exact same server machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The newest version of MCP removes all of this.&lt;/strong&gt; There's no more hello message, and no more special ID to remember. Every single message now carries everything it needs, all on its own. Because of this, any message can be handled by any available server machine. If a client really wants to know what a server can do ahead of time, it can still ask, but it's optional now.&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%2Fe2m37y78l0f5h3y5wq1s.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%2Fe2m37y78l0f5h3y5wq1s.png" alt="Shows requests being routed between multiple server machines." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But if a tool genuinely needs to remember something between steps, the tool hands back a kind of "ticket" or reference number, and the AI model passes that ticket back on the next request. The memory lives in the conversation itself, where everyone can see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The building blocks a server can offer
&lt;/h2&gt;

&lt;p&gt;MCP only allows a server to offer a few specific kinds of things. Keeping this list short and simple makes it much easier for both AI models and apps to understand what's happening.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Building block&lt;/th&gt;
&lt;th&gt;Who starts it&lt;/th&gt;
&lt;th&gt;Who allows it&lt;/th&gt;
&lt;th&gt;What it's for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The AI model decides to call it&lt;/td&gt;
&lt;td&gt;The host decides if it's allowed&lt;/td&gt;
&lt;td&gt;Doing something: sending an email, running a search, creating a ticket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The app decides to use it&lt;/td&gt;
&lt;td&gt;The app decides if it's allowed&lt;/td&gt;
&lt;td&gt;Reading something: a file, a database row, a log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The person picks it directly&lt;/td&gt;
&lt;td&gt;The person chooses it&lt;/td&gt;
&lt;td&gt;A ready-made template, like a saved shortcut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sampling &lt;em&gt;(being phased out)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;The server asks for it&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;The server asks the AI model to write something mid-task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roots &lt;em&gt;(being phased out)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;The client tells the server&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Telling a server which folders it's allowed to touch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A tool is described with a name, a short explanation of what it does, and a list of the information it needs:&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;"create_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Create a new issue in the project's issue tracker."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&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;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"title"&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;"string"&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;"body"&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;"string"&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;"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;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"string"&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="nl"&gt;"required"&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="s2"&gt;"title"&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;
  
  
  6. How a server can still ask you something, even without memory
&lt;/h2&gt;

&lt;p&gt;Sometimes a tool needs to check with you before it finishes. Maybe it wants to confirm something risky, like "are you sure you want to delete this?" But we just said servers don't remember anything between messages anymore. So how does that work?&lt;/p&gt;

&lt;p&gt;The answer is called MRTR, short for Multi Round-Trip Requests. Instead of the server waiting and holding the line open, it just replies right away and says "I need more information first."&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%2Fcj4oacrr2tb4skhv3b60.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%2Fcj4oacrr2tb4skhv3b60.png" alt="Shows how MCP handles user approval for sensitive actions." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's no open connection the whole time. The client just asks the same question again, this time with the answer included. This is simple, and it works well even though the server has no memory at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. A full tool call, from start to finish
&lt;/h2&gt;

&lt;p&gt;Let's walk through one complete example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The host prepares the available tools.&lt;/strong&gt; It already knows which tools the server offers and converts them into the format the AI model expects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The AI model decides to call a tool.&lt;/strong&gt; It chooses &lt;code&gt;create_issue&lt;/code&gt; and fills in the required arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The client sends the request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/mcp&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;MCP-Protocol-Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-07-28&lt;/span&gt;
&lt;span class="na"&gt;Mcp-Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tools/call&lt;/span&gt;
&lt;span class="na"&gt;Mcp-Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create_issue&lt;/span&gt;
&lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer eyJhbGciOi...&lt;/span&gt;

{"jsonrpc":"2.0","id":17,"method":"tools/call",
 "params":{
   "name":"create_issue",
   "arguments":{"title":"Bug: nav bar overflow","labels":["frontend"]}
 }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. The server does the real work by creating the issue and returning the result.&lt;/strong&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="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"id"&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="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resultType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"success"&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="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Created issue #4821: Bug: nav bar overflow"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"structuredContent"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"issueId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4821&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"https://tracker.example.com/issues/4821"&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;&lt;strong&gt;5. The host adds the result back into the conversation. The AI model now knows the issue was created and continues its reasoning.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frj25vhhy854w9i6n20xo.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%2Frj25vhhy854w9i6n20xo.png" alt="Shows how an AI model creates a ticket through MCP" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the whole loop: model, host, client, server, and the real tool being used. This repeats as many times as needed until the AI model has everything it needs to answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Why AI agents need this
&lt;/h2&gt;

&lt;p&gt;You might ask: why not just give the AI model direct access to a normal API? People tried that in the early days, but it ran into real problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every AI model expects a slightly different format.&lt;/strong&gt; With MCP, the tool only needs to describe itself once. Each app then translates that into whatever format its own AI model needs. The tool author doesn't need to learn the details of every AI model out there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools need to be discovered on the fly.&lt;/strong&gt; An AI agent often doesn't know ahead of time which tools it will need. Being able to ask "what tools do you have?" at any time, and get a clear answer, matters a lot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reading data and taking action are very different things&lt;/strong&gt;, and mixing them up is dangerous. MCP keeps them separate on purpose. Reading a file is treated differently than deleting one. This lets the host set a simple, clear rule: reading things can happen quietly, but taking action needs a check first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Someone needs to enforce the rules in one place.&lt;/strong&gt; If every tool has its own custom code, safety checks can easily get missed somewhere. With MCP, every single tool call passes through the same host, so the same safety rules apply every time, no matter which tool is being used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not needing memory matches how these systems are actually used.&lt;/strong&gt; A company might have thousands of people using an agent at the same time, hitting many different tools. Not needing to remember each person's exact connection makes this much easier to run at a large scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Trust and safety: what can go wrong, and how MCP handles it
&lt;/h2&gt;

&lt;p&gt;This is one of the most important parts, and it's often skipped in simple guides.&lt;/p&gt;

&lt;p&gt;MCP treats three sides as possibly untrustworthy toward each other: the person, the AI model and host, and the server. That might sound harsh, but here's why it matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A bad server could try to trick the AI model.&lt;/strong&gt; A tool's description is just text, and that text becomes part of what the AI model reads. A dishonest tool could hide sneaky instructions inside its own description, hoping the AI model follows them without the person knowing. This is called prompt injection. The lesson: never fully trust text that comes from a tool, the same way you wouldn't fully trust a random file someone sent you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A careless client could give a tool too much power.&lt;/strong&gt; If an app hands over a very powerful access key without limiting what it's for, and something goes wrong, the damage could be much bigger than it needed to be. The fix is simple: always give tools the smallest amount of access they actually need, nothing more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never blindly pass one access key to a different tool than it was meant for.&lt;/strong&gt; This sounds obvious, but it's a common mistake. The newest version of MCP adds stronger checks around this, making sure access keys are only used exactly where they were meant to be used.&lt;/p&gt;

&lt;p&gt;The newest version also improves how login and access approval work, closing a few tricky security gaps that experts had found. The overall advice stays the same as always: give every tool the smallest access it needs, don't trust text from a server blindly, and keep a person in the loop for anything risky or hard to undo.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. How MCP grows without breaking older tools
&lt;/h2&gt;

&lt;p&gt;The newest version also introduces a cleaner way for MCP to grow over time. Instead of stuffing every new idea into the core rules, new features can now be added as separate, optional "extensions." Each one has its own name and its own version number, so it can grow and change without breaking everything else.&lt;/p&gt;

&lt;p&gt;Two extensions launched alongside this update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP Apps&lt;/strong&gt; — lets a tool show an actual visual interface, not just text, inside a safe, boxed-off area of the app. Even though it looks different, anything the person does inside that interface still follows the exact same safety and approval rules as a normal tool call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tasks&lt;/strong&gt; — built for work that takes a long time to finish, like a big data job. Instead of waiting the whole time, the app can check back later to see if it's done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bigger idea here is, keep the core of MCP small and simple, and let fancier features grow on the outside, as optional add-ons. This is a common pattern in long-lasting technology.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Where MCP is heading next
&lt;/h3&gt;

&lt;p&gt;MCP is moving toward a smaller core protocol with optional extensions. Enterprise management, long-running tasks, and newer capabilities are being built as add-ons instead of becoming part of the core standard.&lt;/p&gt;

&lt;p&gt;Older features that are being phased out will continue working for at least a year, giving developers time to migrate. At the same time, support for MCP has grown quickly across the industry, making it increasingly likely to become the standard way AI applications connect to external tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;MCP is a shared standard that lets AI apps and outside tools talk to each other safely. It has three parts: the host (which runs the AI model and enforces the rules), the client (a simple messenger), and the server (which offers tools, data, or templates). The newest version removed the need for servers to remember anything between messages, which makes everything easier to run at a large scale. It also added a smart way for tools to ask for confirmation without needing to stay connected the whole time. AI agents need something like MCP because they must discover tools on the fly, keep reading and doing separate, and never fully trust anything a tool sends back without checking it first.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For more such developer content on multiple formats, visit:&lt;br&gt;
&lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>agents</category>
      <category>python</category>
      <category>vickybytes</category>
    </item>
    <item>
      <title>Complete Guide to Context Engineering in LLMs</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Sun, 02 Aug 2026 07:14:02 +0000</pubDate>
      <link>https://dev.to/shresthapandey/complete-guide-to-context-engineering-in-llms-1kp8</link>
      <guid>https://dev.to/shresthapandey/complete-guide-to-context-engineering-in-llms-1kp8</guid>
      <description>&lt;p&gt;Context engineering is one of those terms that you understand when you actually build with LLMs in production. Then it becomes clear that this is the real work of deciding what information the model should see, how that information should be arranged, what should be remembered, what should be retrieved, and what should be kept out of the window entirely. This brings most of the quality improvements in 2026.&lt;/p&gt;

&lt;p&gt;For a long time, LLM output quality was treated as a prompt-writing problem, which was true to a certain point when the use cases were simple and the conversations were short. But once LLMs started powering agents, coding assistants, research workflows, and copilots, the old prompt-first mindset began to break. More often, the model had the wrong context, too much context, stale context, or context arranged in a way that made it hard to use. That is the problem context engineering tries to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What context engineering means
&lt;/h2&gt;

&lt;p&gt;Context engineering is the discipline of shaping the full information payload around an LLM at inference time. The payload includes system instructions, user input, retrieved documents, memory, tool outputs, schemas, summaries, and the conversational history. In other words, the model is reasoning inside a temporary workspace, and the quality of that workspace heavily affects the answer it gives.&lt;/p&gt;

&lt;p&gt;Prompt engineering changes how you ask the model a question, while context engineering changes what the model knows when it answers. A well-written prompt can still fail if the model is missing the relevant facts, overloaded with noise, or forced to reason over stale memories and irrelevant tool outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters now
&lt;/h2&gt;

&lt;p&gt;The reason this topic has exploded in 2026 is that LLM applications are now systems, they read files, call tools, search databases, remember prior state, execute multi-step workflows, and sometimes even hand work off to other agents. As soon as you move into that world, the biggest source of failure becomes context management, not prompting.&lt;/p&gt;

&lt;p&gt;This is especially visible in long-horizon agents. If an agent is working for many steps, it creates its own history, accumulates its own tool results, and gradually fills the window with decisions, partial outputs, and summaries. Without intentional context control, the model starts to lose track of what’s important. Anthropic’s agent guidance for long-running systems highlights this problem and points to compaction, structured notes, and careful memory handling as practical solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four pillars
&lt;/h2&gt;

&lt;p&gt;A useful way to organize context engineering is around four connected actions: write, select, compress, and isolate. This framework captures the full lifecycle of context rather than just the initial prompt.&lt;/p&gt;

&lt;p&gt;Write means storing durable information outside the active window. If something should survive beyond the current turn, it should not live only in chat history. User preferences, task checkpoints, important decisions, stable project facts, and reusable notes all belong in a more persistent store. That can be a memory system, a database, a file, or any structured state layer.&lt;/p&gt;

&lt;p&gt;Select means retrieving only what is relevant for the current step. This is where RAG, semantic search, code search, and memory retrieval matter. Good selection is all about finding the smallest set of evidence that is enough to support the task.&lt;/p&gt;

&lt;p&gt;Compress means reducing context size without losing meaning. This usually involves summaries, pruning old tool outputs, shortening long conversations, and representing repeated information in compact form. Compression is what keeps a system usable after the first few steps.&lt;/p&gt;

&lt;p&gt;Isolate means separating tasks so they do not corrupt each other. A planning context should not be mixed with execution noise. Untrusted text should not sit in the same place as trusted instructions. Different agents or stages should be kept distinct when the workflow gets complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a production pipeline works
&lt;/h2&gt;

&lt;p&gt;In production, context engineering usually looks more like pipeline design. The user request enters the system, then the app classifies the task, fetches relevant sources, trims and ranks them, strips out unnecessary content, adds the minimum required instructions and tool definitions, and then makes the model call. After the call, useful outputs are stored externally so they can be reused later without bloating the live context.&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%2F5kggyqhqyy948yk4xcne.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%2F5kggyqhqyy948yk4xcne.png" alt="Context Engineering Pipeline" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That pipeline is easy to describe, but many teams still skip parts of it. They either retrieve too much and drown the model in text, or retrieve too little and leave it guessing. The best systems tend to be aggressively selective. They trust structure over volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where systems go wrong
&lt;/h2&gt;

&lt;p&gt;The most common mistake is assuming more context automatically means better answers. In practice, that often creates the opposite effect. Long windows can degrade when the context is noisy, poorly ordered, stale, or full of redundant material. The model may technically have the information, but it may not use it well.&lt;/p&gt;

&lt;p&gt;Another classic failure is weak retrieval. If the right facts exist somewhere but are not brought into the window at the right time, the model will improvise. This can be dangerous in coding, research, support, or agent workflows where correctness matters. Tool output has the same problem, if it is not integrated cleanly, the model may ignore it or overvalue it.&lt;/p&gt;

&lt;p&gt;A third issue is context pollution. Once irrelevant text is mixed into the working set, the model can treat it as if it matters. This is why untrusted content, especially retrieved text or user-provided documents, has to be handled carefully. It is not enough to fetch information; you also need to control how that information is presented.&lt;/p&gt;

&lt;h2&gt;
  
  
  What works
&lt;/h2&gt;

&lt;p&gt;The same techniques appear in most practical 2026 guides. Using clear sections for instructions, background, and expected output helps the model understand what each part is for. Short summaries of earlier messages keep important context without including the entire conversation. Breaking information into smaller, relevant chunks makes retrieval more accurate and avoids unnecessary text. Caching also saves time and cost by reusing prompts or templates that do not change.&lt;/p&gt;

&lt;p&gt;The key idea is that more context is not always better. The best context gives the model only the information it needs to complete the task correctly. This may seem simple, but it actually improves performance. Leaving out unnecessary details reduces distractions and helps the model focus on the most relevant information.&lt;/p&gt;

&lt;h2&gt;
  
  
  For agents and coding assistants
&lt;/h2&gt;

&lt;p&gt;This becomes very clear in agentic systems and coding tools. A coding assistant does not need your entire repository in the window to help you fix one bug. It needs the right files, the relevant symbols, the recent diffs, the related tests, and a bit of project-level convention. Good context engineering is about providing exactly the information needed for the task.&lt;/p&gt;

&lt;p&gt;The same idea applies to AI agents that work over long periods. They need ways to save progress, organize memory, compress old information, and separate long-term knowledge from temporary working notes. Recent guidance, including Anthropic's recommendations and other 2026 resources, supports this more structured approach, where the system actively manages context over time instead of treating it as one large block of information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2026 mindset shift
&lt;/h2&gt;

&lt;p&gt;The biggest shift in 2026 is that context engineering is now seen as a complete system design practice. It combines prompt design, retrieval, memory management, tool integration, and state management to help AI models perform reliably. As a result, newer concepts such as retrieval budgeting, context compaction, memory tiering, tool-result pruning, and context isolation have become important parts of modern AI system design.&lt;/p&gt;

&lt;p&gt;At the same time, the field is becoming more realistic about the limits of large context windows. While larger context windows are useful, they do not automatically improve results. The real challenge is selecting and managing the right information instead of simply providing more of it. This is why improving LLM performance is increasingly seen as a systems engineering problem during inference, rather than relying only on building larger models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;If prompt engineering was about wording, context engineering is about design. It asks a harder question: what should the model know right now, and what is the cleanest possible way to make that knowledge available? Once you start thinking in this way, a lot of LLM failures start to look like ordinary information architecture problems. And that is good news, because information architecture is something that engineers can actually improve.&lt;/p&gt;

&lt;p&gt;For more such developer content, visit:&lt;br&gt;
&lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>contextengineering</category>
      <category>vickybytes</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>My First End-to-End Data Pipeline in Databricks</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Fri, 24 Jul 2026 13:40:50 +0000</pubDate>
      <link>https://dev.to/shresthapandey/my-first-end-to-end-data-pipeline-in-databricks-4ajm</link>
      <guid>https://dev.to/shresthapandey/my-first-end-to-end-data-pipeline-in-databricks-4ajm</guid>
      <description>&lt;p&gt;Whenever I searched for resources on Databricks, I found two extremes. Some explained the concepts without showing how they fit together, while others jumped straight into large projects assuming you already knew the basics, but I wanted something in the middle.&lt;/p&gt;

&lt;p&gt;Rather than learning every feature separately, I decided to build a small end-to-end project that covered the fundamentals of data engineering in Databricks. I wanted to understand how data moves through the platform, how different components connect, and why concepts like Delta Lake and the Medallion Architecture are used so often.&lt;/p&gt;

&lt;p&gt;For this project, I used three simple retail datasets containing customers, products, and orders. Starting with these CSV files, I built a pipeline that reads the data using PySpark, stores it in Delta Lake, organizes it into Bronze, Silver, and Gold layers, analyzes it with Spark SQL, creates a dashboard, and finally automates the entire workflow using Databricks Jobs.&lt;/p&gt;

&lt;p&gt;If you're just getting started with Databricks, this project covers many of the concepts you'll use in real-world workflows while keeping the implementation simple enough to follow.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Databricks?
&lt;/h1&gt;

&lt;p&gt;Before starting with code, I explored the Databricks workspace to understand what the platform offers. The interesting part was how everything required for a data engineering workflow is available in one place.&lt;/p&gt;

&lt;p&gt;The Workspace is where notebooks live, the Catalog helps organize data assets, the SQL Editor is used for writing analytical queries, and Jobs allows notebooks to run automatically on a schedule. Having these components integrated into a single platform makes it much easier to move from raw data to analytics without switching between multiple tools.&lt;/p&gt;

&lt;p&gt;Another concept that appears everywhere in Databricks is the &lt;strong&gt;Lakehouse Architecture&lt;/strong&gt;. Raw data needs to be stored safely, transformed into cleaner datasets, and eventually prepared for reporting or dashboards. The Lakehouse approach supports all these stages while using Delta Lake as the storage layer, which brings features like reliable transactions and version history.&lt;/p&gt;

&lt;p&gt;This project follows that same approach from start to finish, which makes it easier to understand why the Lakehouse architecture has become a common choice for modern data engineering.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting Up the Environment
&lt;/h1&gt;

&lt;p&gt;For development, I used a Databricks Notebook and uploaded my datasets into a Databricks Volume. I used Python and PySpark for data ingestion and transformations, switched to SQL for analysis, and added Markdown cells to organize different sections of the notebook.&lt;/p&gt;

&lt;p&gt;The datasets were uploaded into a &lt;strong&gt;Databricks Volume&lt;/strong&gt;, making them easy to access from the notebook.&lt;/p&gt;

&lt;p&gt;The project uses three CSV files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;customers.csv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orders.csv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;products.csv&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping the dataset small made it much easier to focus on understanding the workflow rather than spending time cleaning complex data.&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%2Ffjdshe88m7bzea3j0qjg.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%2Ffjdshe88m7bzea3j0qjg.png" alt="Databricks Volume containing the uploaded CSV files" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Building the Data Pipeline with PySpark
&lt;/h1&gt;

&lt;p&gt;With the environment ready, the next step was reading the datasets into Databricks using PySpark. Since the files were already uploaded to a Volume, accessing them from the notebook was simple.&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;customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/Volumes/workspace/default/retail_data/customers.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;header&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;inferSchema&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;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/Volumes/workspace/default/retail_data/orders.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;header&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;inferSchema&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;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/Volumes/workspace/default/retail_data/products.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;header&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;inferSchema&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;p&gt;PySpark automatically inferred the schema, so I didn't have to manually define the data types for every column. After loading each dataset, I used &lt;code&gt;display()&lt;/code&gt; to verify that everything had been imported correctly before moving on to transformations.&lt;/p&gt;

&lt;h1&gt;
  
  
  Storing the Raw Data with Delta Lake
&lt;/h1&gt;

&lt;p&gt;Once the CSV files were loaded, I converted them into Delta tables. This was the beginning of the &lt;strong&gt;Bronze layer&lt;/strong&gt; in the Medallion Architecture.&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;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overwrite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;saveAsTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bronze_customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overwrite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;saveAsTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bronze_orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overwrite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;saveAsTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bronze_products&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;The Bronze layer stores the data exactly as it arrives. At this stage, no cleaning or transformations are applied because it's useful to preserve the original data for auditing, debugging, or reprocessing later.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cleaning the Data in the Silver Layer
&lt;/h1&gt;

&lt;p&gt;To create the Silver layer, I performed some simple transformations on the orders dataset. For this, I removed duplicate records and filled missing values.&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;silver_orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;bronze_orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dropDuplicates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;na&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cleaned dataset was then stored as another Delta table. This stage represents a common pattern in data engineering. Cleaning and validating data before using it for analysis helps improve the reliability of downstream reports and dashboards.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating the Gold Layer
&lt;/h1&gt;

&lt;p&gt;The final step in the transformation process was creating a dataset that could be used directly for analysis. I joined the customer, product, and order tables into a single DataFrame and calculated the revenue for each order.&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;pyspark.sql.functions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;

&lt;span class="n"&gt;gold_sales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;silver_orders&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bronze_customers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_id&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bronze_products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&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="nf"&gt;withColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;col&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I saved the result as a Delta table.&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;gold_sales&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overwrite&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="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&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="nf"&gt;saveAsTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gold_sales&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;At this point, raw CSV files gradually turned into a structured dataset that could answer business questions with just a few SQL queries. It also demonstrated how PySpark and Delta Lake work together. PySpark handled the transformations, while Delta Lake provided a reliable storage layer for every stage of the pipeline.&lt;/p&gt;

&lt;h1&gt;
  
  
  Querying the Data with Spark SQL
&lt;/h1&gt;

&lt;p&gt;Once the Gold table got ready, I switched to SQL to explore the data. It was really feasible to move between PySpark and SQL. Transforming data with PySpark felt simpler, while SQL made it simple to answer business questions without writing additional Python code.&lt;/p&gt;

&lt;p&gt;For this project, I used the &lt;strong&gt;Databricks SQL Editor&lt;/strong&gt; and a &lt;strong&gt;SQL Warehouse&lt;/strong&gt; to run analytical queries. SQL Warehouses are optimized for interactive queries and reporting workloads, making them a good choice when exploring datasets or building dashboards.&lt;/p&gt;

&lt;p&gt;I started with: &lt;strong&gt;Which products generated the highest revenue?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;revenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_revenue&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;gold_sales&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_revenue&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result highlighted the products contributing the most revenue.&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%2Fyboflonxvk2h3vgj1l98.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%2Fyboflonxvk2h3vgj1l98.png" alt="SQL query and its output in the SQL Editor" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating a Dashboard
&lt;/h1&gt;

&lt;p&gt;After running the query, I created a simple bar chart directly within Databricks. The dashboard visualized revenue by product, making the results much easier to interpret than reading rows in a table.&lt;/p&gt;

&lt;p&gt;Databricks lets you create charts from SQL query results in just a few clicks, which is useful for quickly sharing insights with teammates or stakeholders.&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%2F7cks35rlbs3tqvok48ld.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%2F7cks35rlbs3tqvok48ld.png" alt="Dashboard showing total revenue by product" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Automating the Pipeline with Databricks Jobs
&lt;/h1&gt;

&lt;p&gt;Running a notebook manually is useful during development, though production pipelines usually need to execute on a schedule.&lt;/p&gt;

&lt;p&gt;To complete the workflow, I created a &lt;strong&gt;Databricks Job&lt;/strong&gt; for my notebook. The setup involved selecting the notebook, attaching the compute resource, and defining a schedule. Databricks also provides options for retries and notifications, making it easier to monitor automated workloads as projects become more complex.&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%2Fkc0e9n3cpfrulsmpzrlz.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%2Fkc0e9n3cpfrulsmpzrlz.png" alt="Databricks Job configuration" width="799" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Exploring Delta Time Travel
&lt;/h1&gt;

&lt;p&gt;One feature I wanted to try before finishing the project was &lt;strong&gt;Delta Time Travel&lt;/strong&gt;. Every change made to a Delta table is recorded, allowing previous versions to be inspected whenever required.&lt;/p&gt;

&lt;p&gt;I viewed the history of my Gold table using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DESCRIBE&lt;/span&gt; &lt;span class="n"&gt;HISTORY&lt;/span&gt; &lt;span class="n"&gt;gold_sales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feature can be especially useful when debugging pipelines or recovering from accidental updates.&lt;/p&gt;

&lt;h1&gt;
  
  
  A Quick Look at Unity Catalog
&lt;/h1&gt;

&lt;p&gt;Since all my tables were created inside Databricks, I also explored &lt;strong&gt;Unity Catalog&lt;/strong&gt;, which serves as the central place for managing data assets. It organizes tables, volumes, and other resources, making them easier to discover and manage across projects.&lt;/p&gt;

&lt;p&gt;While this project focused on the fundamentals, Unity Catalog also supports governance features such as permissions and data lineage, which become increasingly important in collaborative environments.&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%2Fwi44brdbqk0f01282556.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%2Fwi44brdbqk0f01282556.png" alt="Unity Catalog showing the Bronze, Silver, and Gold tables" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Starting with raw CSV files and gradually moving through Bronze, Silver, and Gold layers showed how data evolves before reaching analysts or dashboards. PySpark handled the transformations, Delta Lake provided a reliable storage layer, SQL made analysis simpler, and Databricks Jobs completed the workflow by automating the notebook.&lt;/p&gt;

&lt;p&gt;The project is small, but it covers many of the core ideas you'll encounter while working with Databricks. It helped me understand why these concepts exist rather than simply memorizing their definitions.&lt;/p&gt;

&lt;p&gt;If you're getting started with Databricks, I'd recommend building a similar end-to-end project. It doesn't require a large dataset, and you'll come away with a much clearer understanding of how the platform works.&lt;/p&gt;

&lt;h1&gt;
  
  
  GitHub Repository
&lt;/h1&gt;

&lt;p&gt;The complete notebook, datasets, and project files are available here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 GitHub:&lt;/strong&gt; &lt;em&gt;&lt;a href="https://github.com/Shresthap21/Databricks-pipeline" rel="noopener noreferrer"&gt;Databricks-pipeline&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you build on top of this project or have suggestions for improving the pipeline, I'd love to hear your thoughts.&lt;/p&gt;

&lt;p&gt;For more such project ideas, visit:&lt;br&gt;
&lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>databricks</category>
      <category>pyspark</category>
      <category>vickybytes</category>
      <category>sql</category>
    </item>
    <item>
      <title>The Debugger Is Lying to You Sometimes</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Tue, 21 Jul 2026 20:46:07 +0000</pubDate>
      <link>https://dev.to/shresthapandey/the-debugger-is-lying-to-you-sometimes-2nb8</link>
      <guid>https://dev.to/shresthapandey/the-debugger-is-lying-to-you-sometimes-2nb8</guid>
      <description>&lt;p&gt;Debugging should feel like the safest part of programming. Sometimes it does, but sometimes the debugger makes everything look fine while the real bug is hiding somewhere else, which is one of the most frustrating parts of development. &lt;/p&gt;

&lt;p&gt;Everything looks correct, still the app breaks. This happens because the debugger only shows one moment in time. Software keeps moving, values change, requests come back late, state updates in the background, and the bug may already have shifted by the time you look at it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When everything looks correct
&lt;/h2&gt;

&lt;p&gt;A lot of confusing bugs start here. The code looks fine in the debugger, but the app still behaves badly. This usually means the problem is not in the line you are looking at. It may be in what happened before that line, or after it, or somewhere completely different.&lt;/p&gt;

&lt;p&gt;This is very common in frontend work. A component may show the right props, but the state is already stale. A hook may run with old data. A callback may still be using an earlier value. In backend code, a request may arrive at the right place but with data that was changed by another process. In both cases, the debugger is honest, but only for that second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why state causes trouble
&lt;/h2&gt;

&lt;p&gt;State is where many bugs hide. It changes quietly, and sometimes it changes in more than one place. A local variable may look perfect, but the real issue is the state that was copied earlier and never updated. A UI may look correct on screen, while the internal data is out of sync.&lt;/p&gt;

&lt;p&gt;The issue is usually the flow around it. That's also the reason why bugs in modern apps can feel harder than they should. The code may be doing exactly what you wrote, but not what you thought it would do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logs can confuse you
&lt;/h2&gt;

&lt;p&gt;Logs help a lot, but they can also mislead you if you trust them too much. A log only shows what you decided to print. If the important branch never ran, the log will not tell you that. If a promise resolved later, you may miss the real order of events. If an error happened before your log line, the message can give you the wrong idea.&lt;/p&gt;

&lt;p&gt;That is why logs work best when they show movement. I usually find them most useful when they capture input, output, and any point where the program changes direction. A single log line not always tells the full story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hot reload and cache make it worse
&lt;/h2&gt;

&lt;p&gt;Sometimes the bug is not even in the code you think you are running. Hot reload can keep old state alive. Browser cache can hold on to old files. Service workers can serve stale assets. A local build can look updated while the browser is still running something older.&lt;/p&gt;

&lt;p&gt;These bugs are annoying because they make you doubt yourself. You change the code, refresh the page, and still see the old behavior. It feels like the debugger or the code is broken, when the real issue is often the environment. That is why clearing cache, restarting the dev server, or opening a clean session fixes more problems than people expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local and production are different
&lt;/h2&gt;

&lt;p&gt;A bug on your machine is not always the same bug users see in production. Something that behaves fine locally can fail under real load or with real user actions.&lt;/p&gt;

&lt;p&gt;Local debugging is only part of the job. It helps you narrow things down, but it does not always show the whole picture. Production needs its own signals like logs, error tracking, metrics, traces, and good reporting. &lt;/p&gt;

&lt;h2&gt;
  
  
  What helps
&lt;/h2&gt;

&lt;p&gt;When a bug refuses to show itself, I think it helps to stop looking only at the line in front of you.&lt;/p&gt;

&lt;p&gt;A few simple habits make this easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watch the full flow rather than just one line.&lt;/li&gt;
&lt;li&gt;Check values before and after async work.&lt;/li&gt;
&lt;li&gt;Clear cache when behavior looks stale.&lt;/li&gt;
&lt;li&gt;Restart the app when hot reload feels suspicious.&lt;/li&gt;
&lt;li&gt;Add temporary logs around the change in state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These small steps reveal what the debugger is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;The debugger is useful, but it is not the whole truth. It shows you one moment, and sometimes that moment is not the one that matters. Real debugging is more about understanding flow, state, timing, and environment. Once you start thinking that way, hard bugs become less mysterious.&lt;/p&gt;

&lt;p&gt;For more such developer content, visit: &lt;br&gt;
&lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;https://vickybytes.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>vickybytes</category>
      <category>coding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How Beginner Developers Can Find Great Project Ideas</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Mon, 06 Jul 2026 18:12:33 +0000</pubDate>
      <link>https://dev.to/shresthapandey/how-beginner-developers-can-find-great-project-ideas-4kia</link>
      <guid>https://dev.to/shresthapandey/how-beginner-developers-can-find-great-project-ideas-4kia</guid>
      <description>&lt;p&gt;Every beginner developer hits the same issue at some point. You learn a few basics, finish a tutorial, and then you have no idea what to build next. That gap can feel bigger than learning the code itself, because now the question is not “How do I write this?” but “What should I build at all?”&lt;/p&gt;

&lt;p&gt;This article is for that moment. I want to make it simple, practical, and useful, because project ideas do not need to be too advanced to be valuable. A good project is one that teaches you something, keeps you going, and gives you enough confidence to build the next one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why project ideas are important
&lt;/h2&gt;

&lt;p&gt;There’s a common thing that I have noticed in most of the beginners, that is, watching too many tutorials. Tutorials are helpful, but actual learning starts when you try to build something on your own. That is when you start facing real decisions, small bugs, unclear logic, and the feeling of connecting different parts into one working product.&lt;/p&gt;

&lt;p&gt;That is one of the reasons why project ideas matter so much. The right idea gives you direction, but it also gives you energy. When the project feels too huge, you get stuck. When it feels too small or boring, you stop caring. The sweet spot is a project that feels possible and still a little exciting.&lt;/p&gt;

&lt;p&gt;This matters even more today. Tools like ChatGPT or Copilot can help you write code faster, but that doesn't solve the real problem beginners have. Writing the code was never the hard part for long but knowing what to build is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with problems you already know
&lt;/h2&gt;

&lt;p&gt;The easiest project ideas often come from your own life. Think about small things you do every day that feel annoying, repetitive, or messy. A simple to-do list, habit tracker, note saver, expense log, study planner, or meal planner can all become strong beginner projects if you build them well.&lt;/p&gt;

&lt;p&gt;This works because the problem is already familiar to you. You do not have to invent a fake use case or force a complicated feature list. You already know what the app should do, what feels useful, and what would make it easier to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Borrow ideas from tutorials, then make them yours
&lt;/h2&gt;

&lt;p&gt;Tutorial projects are not bad. In fact, they are one of the best ways to learn. But you need to avoid copying them word for word and calling it done. If you followed a weather app tutorial, try changing the design, adding saved cities, showing alerts, or making the app work for your own city list.&lt;/p&gt;

&lt;p&gt;This small change matters a lot. It turns a passive learning exercise into a valuable project. You still get the guidance, but you also start making choices on your own, and that is where confidence starts growing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at everyday tools
&lt;/h2&gt;

&lt;p&gt;Another easy way to find ideas is to look at tools you already use. Think about apps for tasks, reminders, shopping lists, expense tracking, journaling, or learning. These tools are popular because they solve simple problems clearly, and beginner developers can build smaller versions of them without needing a huge team.&lt;/p&gt;

&lt;p&gt;You do not need to recreate the full product. A clean, focused version is enough. A mini version of a notes app or a simple budget tracker can teach you a lot more than a random overcomplicated idea that you never finish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn one feature into one project
&lt;/h2&gt;

&lt;p&gt;Beginners often make the mistake of thinking a project needs many features to be impressive. It does not. A tiny, focused project is often better because it is easier to finish and easier to understand. For example, one feature can become one project. A form that saves data. A search bar that filters results. A login page with validation. A dashboard that shows one useful metric. When you build around one clear action, the project feels manageable and still useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try something small with AI in it
&lt;/h2&gt;

&lt;p&gt;You don't need to build a big AI product. A small one still teaches you a lot, like how to call an API, handle a response, and manage what's happening on screen. A few simple ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tool that shortens long articles or text&lt;/li&gt;
&lt;li&gt;Something that turns your notes into flashcards&lt;/li&gt;
&lt;li&gt;A small tool that answers questions from a PDF you upload&lt;/li&gt;
&lt;li&gt;A journal app that gives you a short reply based on what you wrote&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple, working version of any of these makes a solid beginner project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use communities for inspiration
&lt;/h2&gt;

&lt;p&gt;If your own ideas feel stuck, look at what other beginners are building. GitHub, Dev.to, Reddit, Discord communities, hackathon submissions, project labs and open-source repositories can all give you fresh direction. This way you start to notice patterns, problems, and styles of projects that keep appearing. You will often find that many useful ideas are small variations of the same core concept. Most great beginner projects are not original inventions, but they are thoughtful versions of common ideas with a personal twist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Think in “versions”
&lt;/h2&gt;

&lt;p&gt;A lot of beginners wait for the perfect idea, that usually delays everything. A better way to think is in versions. Version one can be simple and ugly, as long as it works. Version two can improve the design, and version three can add one or two stronger features. This way of thinking helps you start faster. It also keeps you from quitting because the idea feels too ambitious. You are not building the final version of a startup. You are building something that helps you learn, ship, and improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the project useful to someone
&lt;/h2&gt;

&lt;p&gt;A project becomes more meaningful when it helps a real person, even in a small way. That person can be you, a friend, a student, or a small community. When you know who it is for, the idea becomes easier to shape.&lt;/p&gt;

&lt;p&gt;For example, a revision planner for students, a simple content calendar for creators, or a shared checklist for a small team already has a clear purpose. The moment you know the user, you start building something that makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple way to choose
&lt;/h2&gt;

&lt;p&gt;If you still do not know what to build, use this simple test.&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can I explain this idea in one sentence?&lt;/li&gt;
&lt;li&gt;Can I build a first version in a reasonable amount of time?&lt;/li&gt;
&lt;li&gt;Will I learn something new from it?&lt;/li&gt;
&lt;li&gt;Do I care enough to finish it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the answer is yes to most of these, you probably have a good project idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to avoid
&lt;/h2&gt;

&lt;p&gt;Try not to start with ideas that are too broad. “Make a social media app” sounds exciting, but it often turns into confusion fast. Huge projects can be motivating at first, then frustrating once the scope starts growing. Also avoid choosing an idea just because it sounds impressive. The best beginner projects are often the ones that teach you core skills clearly. A simple app that you actually finish is far more valuable than a complex one that stays half-done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Creator Labs fits in
&lt;/h2&gt;

&lt;p&gt;If you are someone who learns better with guidance, structured challenge spaces can help a lot. That is one reason places like &lt;strong&gt;&lt;a href="https://vickybytes.com/creator-labs" rel="noopener noreferrer"&gt;Creator Labs&lt;/a&gt; by &lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;VickyBytes&lt;/a&gt;&lt;/strong&gt; can be useful for beginners who want direction, ideas, and a nudge to actually build. It is easier to stay consistent when you have a place that keeps you thinking in terms of projects, not just tutorials.&lt;/p&gt;

&lt;p&gt;I like that kind of setup because it helps beginners move from “I know the basics” to “I can build something real.” That transition is where most people get stuck, and that is where the right support can make a difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;Finding a great project idea is all about noticing small problems, starting with something simple, and building in a way that keeps you moving. The best ideas are usually the ones you can explain clearly, start quickly, and finish without losing interest.&lt;/p&gt;

&lt;p&gt;If you are a beginner, give yourself permission to build small. A finished small project teaches more than an unfinished big one, and every strong developer starts by making that first real thing&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>creatorlabs</category>
      <category>techprojects</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>The Best AI Tools for Developers in 2026</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Sat, 04 Jul 2026 12:46:52 +0000</pubDate>
      <link>https://dev.to/shresthapandey/the-best-ai-tools-for-developers-in-2026-2ad7</link>
      <guid>https://dev.to/shresthapandey/the-best-ai-tools-for-developers-in-2026-2ad7</guid>
      <description>&lt;p&gt;AI tools for developers are everywhere right now. Some are genuinely useful, some are overhyped, and some only fit a very specific kind of workflow. AI has moved from basic autocomplete into something that can help with debugging, refactoring, code review, app scaffolding, and parts of deployment too.&lt;/p&gt;

&lt;p&gt;This post looks at the tools that keep coming up in real developer conversations and the kinds of tasks they actually help with. The goal is to know which tool makes your day-to-day work smoother, faster, and less annoying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this topic
&lt;/h2&gt;

&lt;p&gt;A few years ago, most AI coding tools were mostly about completing lines of code. In 2026, the conversation is much broader. Developers are using AI for explanation, planning, code generation, debugging, review, and workflow cleanup.&lt;/p&gt;

&lt;p&gt;That matters because in software development, a lot of the job is reading unfamiliar code, making safe changes, reviewing pull requests, and moving through repetitive work without losing focus. The best AI tools are the ones that help with those parts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as a useful AI dev tool
&lt;/h2&gt;

&lt;p&gt;For this article, I’m focusing on tools that help with real development work. That includes code completion, editing, reasoning through bugs, generating app scaffolds, reviewing code, and speeding up routine tasks. I’m also keeping the definition practical. A product can call itself “AI for developers”, but that does not automatically make it useful. The tools that matter are the ones that save time without getting in the way of quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The editor-first options
&lt;/h2&gt;

&lt;p&gt;Cursor is one of the most talked-about AI-first coding environments right now. It gives you an AI-driven editor experience rather than just a plugin layered on top of an existing setup. That makes it appealing if you want the assistant to feel closely connected to the code you’re working on.&lt;/p&gt;

&lt;p&gt;GitHub Copilot still makes a lot of sense for developers who want AI help without changing their habits too much. Its strength is that it fits naturally into workflows people already use every day. For many developers, that familiarity matters more than having the newest interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reasoning-heavy helpers
&lt;/h2&gt;

&lt;p&gt;Claude Code comes up often in conversations about harder coding tasks. People tend to reach for it when they want help reading large codebases, untangling bugs, or working through bigger refactors. That kind of tool is useful because a lot of developer time goes into figuring out what a system is doing before writing the fix. When the problem is messy, a tool that helps you think more clearly can be worth a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The terminal-friendly choices
&lt;/h2&gt;

&lt;p&gt;Aider stands out because it fits well into a Git-based workflow. It works for developers who like staying in the terminal and want AI edits tied directly to the repository and change history.&lt;/p&gt;

&lt;p&gt;This category matters because not every developer wants a full AI-first IDE. Some people prefer smaller tools that feel close to the command line and less disruptive to the way they already work. For them, a terminal-friendly assistant can feel much more natural.&lt;/p&gt;

&lt;h2&gt;
  
  
  The newer all-round contenders
&lt;/h2&gt;

&lt;p&gt;Windsurf is another name that keeps showing up in 2026 AI tool conversations. It sits in the same broad category as other AI-first coding environments, so the real decision usually comes down to workflow fit, pricing, and how the tool behaves in practice.&lt;/p&gt;

&lt;p&gt;The bigger point here is that the market is no longer about one dominant product. It’s about which tool removes the most friction from your own workflow. That is why people keep comparing these tools by use case rather than treating one of them as the universal answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What these tools do well
&lt;/h2&gt;

&lt;p&gt;The most useful AI tools are the ones that remove friction from repetitive parts of development. That usually means generating a first draft faster, explaining unfamiliar code, helping with small edits, accelerating debugging, or handling tedious transformations. That kind of help can make a real difference during a busy week. It gives you more room to focus on the parts that need judgment, like architecture, testing, and code quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to be careful about
&lt;/h2&gt;

&lt;p&gt;AI can make people feel productive very quickly. That can be helpful, and it can also be risky if you move faster than your understanding. Generated code still needs review. Suggestions still need testing. If a tool helps you ship faster without helping you understand the changes, that speed can become a problem later when the code needs maintenance or collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  My honest take
&lt;/h2&gt;

&lt;p&gt;If you are choosing one tool to start with, pick the one that fits how you already work. If you want an AI-first editor, Cursor is a strong place to look. If you want a familiar workflow with low friction, Copilot still makes sense. If you want help thinking through harder code, Claude Code is worth exploring. If you like terminal-based work, Aider is a practical option.&lt;/p&gt;

&lt;p&gt;I would not frame this as a one-tool-fits-all situation. Different tools solve different problems, and the best one is the one that helps you ship better work with less friction. The best AI tools for developers in 2026 are not always the loudest ones, they are the ones that make development smoother, clearer, and less repetitive without getting in the way of quality.&lt;/p&gt;

</description>
      <category>devtools</category>
      <category>ai</category>
      <category>productivity</category>
      <category>vickybytes</category>
    </item>
    <item>
      <title>How to Deploy Your ML Model to AWS (Step-by-Step Guide)</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Mon, 22 Jun 2026 07:21:40 +0000</pubDate>
      <link>https://dev.to/shresthapandey/how-to-deploy-your-ml-model-to-aws-step-by-step-guide-af9</link>
      <guid>https://dev.to/shresthapandey/how-to-deploy-your-ml-model-to-aws-step-by-step-guide-af9</guid>
      <description>&lt;p&gt;I've trained more ML models than I've deployed. There's something comforting about the local loop—&lt;code&gt;model.fit()&lt;/code&gt;,&amp;nbsp;&lt;code&gt;model.evaluate()&lt;/code&gt;, hitting 94% accuracy, then staring at the screen wondering, "Okay, how do I make this actually useful?"&lt;/p&gt;

&lt;p&gt;If you're stuck there right now, this guide will help.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note:&amp;nbsp;I wrote this based on AWS documentation and standard SageMaker patterns. If you try it, drop a comment about what worked (or broke).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What You Need Before Starting&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AWS account with SageMaker enabled&lt;/li&gt;
&lt;li&gt;A trained model saved as&amp;nbsp;&lt;code&gt;model.pkl&lt;/code&gt;&amp;nbsp;(or&amp;nbsp;&lt;code&gt;.joblib&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;requirements.txt&lt;/code&gt;&amp;nbsp;with your dependencies&lt;/li&gt;
&lt;li&gt;Python 3.8+ installed&lt;/li&gt;
&lt;li&gt;AWS CLI configured (&lt;code&gt;aws configure&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Save Your Model&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;joblib&lt;/span&gt;
&lt;span class="n"&gt;joblib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model.pkl&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;Create a&amp;nbsp;&lt;code&gt;requirements.txt&lt;/code&gt;&amp;nbsp;file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sklearn==1.2.0
pandas==1.5.0
numpy==1.23.0`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep both files in the same folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Upload to S3&lt;/strong&gt;
&lt;/h2&gt;



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

&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;bucket_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;my-unique-ml-bucket-12345&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;# Make this unique
&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateBucketConfiguration&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;LocationConstraint&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;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model.pkl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;models/model.pkl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;requirements.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;models/requirements.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model_s3_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/models/model.pkl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Write Your Inference Script&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Save this as&amp;nbsp;&lt;code&gt;inference.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;joblib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;model_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;joblib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;model.pkl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;input_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content_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;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;features&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unsupported content type: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_data&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="k"&gt;return&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;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;output_fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;predictions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These four functions are what SageMaker calls when someone hits your endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4: Deploy Using Python SDK&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Run this in a Python script:&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;sagemaker.sklearn.model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SKLearnModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sagemaker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_execution_role&lt;/span&gt;

&lt;span class="n"&gt;sklearn_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SKLearnModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model_s3_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_execution_role&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;instance_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;ml.m5.large&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;entry_point&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inference.py&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;py_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;py3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sklearn_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;initial_instance_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instance_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;ml.m5.large&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_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;my-model-endpoint&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 takes 5–10 minutes. You'll see&amp;nbsp;&lt;code&gt;Creating&lt;/code&gt;&amp;nbsp;→&amp;nbsp;&lt;code&gt;In Service&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5: Test Your Endpoint&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sagemaker-runtime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;EndpointName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-model-endpoint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;features&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="mf"&gt;5.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;]]})&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&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;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see&amp;nbsp;&lt;code&gt;{'predictions': [...]}&lt;/code&gt;, it worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6: Clean Up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Endpoints cost money even when idle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws sagemaker delete-endpoint &lt;span class="nt"&gt;--endpoint-name&lt;/span&gt; my-model-endpoint
aws sagemaker delete-endpoint-config &lt;span class="nt"&gt;--endpoint-config-name&lt;/span&gt; my-model-endpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Common Errors (And Fixes)&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Error&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NoCredentialsError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run&amp;nbsp;&lt;code&gt;aws configure&lt;/code&gt;&amp;nbsp;again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InvalidRoleException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IAM role needs S3 + SageMaker permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ModelError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check&amp;nbsp;&lt;code&gt;inference.py&lt;/code&gt;&amp;nbsp;for missing imports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint stuck on&amp;nbsp;&lt;code&gt;Creating&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Wait 5–10 more minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Your IAM role needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;s3:GetObject&lt;/code&gt;,&amp;nbsp;&lt;code&gt;s3:PutObject&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sagemaker:CreateModel&lt;/code&gt;,&amp;nbsp;&lt;code&gt;sagemaker:CreateEndpoint&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cost Breakdown&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ml.m5.large&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~$0.20/hour (~$6/month if 24/7)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 storage&lt;/td&gt;
&lt;td&gt;~$0.02/GB/month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Delete when not using. I've seen $50 surprises from idle endpoints.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Verify This Before You Trust It&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you're following this, check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS SDK version&lt;/strong&gt;&amp;nbsp;— Run&amp;nbsp;&lt;code&gt;pip show boto3 sagemaker&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM role permissions&lt;/strong&gt;&amp;nbsp;— Biggest blocker is usually missing permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Region mismatch&lt;/strong&gt;&amp;nbsp;— S3 bucket region must match SageMaker region&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inference.py imports&lt;/strong&gt;&amp;nbsp;— Make sure&amp;nbsp;&lt;code&gt;os&lt;/code&gt;,&amp;nbsp;&lt;code&gt;joblib&lt;/code&gt;,&amp;nbsp;&lt;code&gt;numpy&lt;/code&gt;&amp;nbsp;are installed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If something breaks, comment below with the error. I'll update this guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Final Thoughts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Deploying ML feels intimidating until you do it once. SageMaker handles most of the complexity. You just upload your model to S3, point SageMaker at it, and deploy.&lt;/p&gt;

&lt;p&gt;I've trained models that sat on my laptop for months because I didn't know how to deploy them. Now I tell people: "Just run this script, it's not that hard."&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're building something with this, drop a comment. I love seeing what people deploy.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>automation</category>
      <category>cloud</category>
      <category>productivity</category>
    </item>
    <item>
      <title>70B AI Model Runs on 8GB Laptop</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Tue, 16 Jun 2026 18:29:25 +0000</pubDate>
      <link>https://dev.to/shresthapandey/70b-ai-model-runs-on-8gb-laptop-445o</link>
      <guid>https://dev.to/shresthapandey/70b-ai-model-runs-on-8gb-laptop-445o</guid>
      <description>&lt;p&gt;You needed a $100,000 server to run huge AI models. Now you can do it on a regular laptop. One developer figured out how, and it changes everything for students, developers, and small companies who want to use AI without breaking the bank.&lt;/p&gt;

&lt;p&gt;A few years ago, running LLaMA 70B required serious hardware, multiple GPUs, 80GB RAM per GPU., a server rack costing more than a car, due to which most people couldn't touch it. You either worked at a big tech company with a data budget, or you couldn't run these models at all.&lt;/p&gt;

&lt;p&gt;In 2026, you can run the same model on a laptop with 8GB RAM. The laptop you bought three years ago. The one on your desk right now and it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Happened&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A developer uploaded something to GitHub called AirLLM. The README said: "Run 70B models on 8GB RAM. No GPU required." That's the whole pitch. &lt;/p&gt;

&lt;p&gt;Developers downloaded it. They tested it on old laptops and budget computers. Even on machines that should not work, and it worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How It Works&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A 70B model takes about 140GB of RAM normally. Even compressed to 4-bit, you still need 35GB. Mostly, laptops don't have that.&lt;/p&gt;

&lt;p&gt;AirLLM gets it down to 8GB. It loads the model differently. Instead of putting everything in RAM at once, it loads parts. When you ask it something, it loads the layers it needs, answers, then swaps them out for the next layers.&lt;/p&gt;

&lt;p&gt;Like reading a book page by page instead of holding all 1,000 pages at once. AirLLM does this with the model. The model is still 70 billion parameters. It's still smart but it never needs all that memory at the same time.&lt;/p&gt;

&lt;p&gt;The technique uses memory mapping and layer swapping. Both are old ideas but putting them together in one tool is what made it work.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Is It Fast?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;No. Running a 70B model on 8GB RAM from a laptop is slower than running it on a server. You're trading speed for getting it to work at all.&lt;/p&gt;

&lt;p&gt;On a 2021 MacBook with 8GB RAM, AirLLM generates about 3-5 tokens per second. That's readable. You can chat with it and ask questions, which was not instant, but still usable.&lt;/p&gt;

&lt;p&gt;On a faster laptop with 16GB RAM? Maybe 8-12 tokens per second. Close to real-time. &lt;br&gt;
On a server with a GPU? 50-100 tokens per second. That's the speed people expect.&lt;/p&gt;

&lt;p&gt;So AirLLM is slower, but it works on computers that are not expected to work. &lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Who Can Use This?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Students don't need a $10,000 computer to learn AI. They can run huge models on the laptop their parents gave them, which removes the biggest barrier to learning.&lt;/p&gt;

&lt;p&gt;Developers can test AI locally without sending data to the cloud. Their code stays on their machine and their questions stay private.&lt;/p&gt;

&lt;p&gt;Small companies don't need to rent GPU servers from AWS or Google Cloud. They can run models on regular computers. That saves thousands of dollars every month.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;What Models Work?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AirLLM supports LLaMA 2 70B, Mistral 7B, Gemma 2 27B, and Falcon 180B if you have more RAM.&lt;/p&gt;

&lt;p&gt;The 70B models are the sweet spot. They are big enough to be smart and small enough to fit on a laptop when compressed.&lt;/p&gt;

&lt;p&gt;You can also run smaller models faster. A 7B model on AirLLM runs at 20-30 tokens per second on a regular laptop, which is instant.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;The Tradeoffs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Speed is slower: A 70B model on a server is 10-20x faster. If you need speed for production, AirLLM is not for you.&lt;/p&gt;

&lt;p&gt;Quality drops a bit: The model is compressed to 4-bit, which means less precision. But it still answers well and makes sense.&lt;/p&gt;

&lt;p&gt;The model takes about 35GB of disk space. So your laptop gets hot and the fan gets loud, maybe after 10 minutes.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;How to Run It&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You need Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bashpip &lt;span class="nb"&gt;install &lt;/span&gt;airllm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the model from Hugging Face:&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;pythonfrom&lt;/span&gt; &lt;span class="n"&gt;airllm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AirLLM&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AirLLM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;meta-llama/Llama-2-70b-hf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;response&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is quantum computing?&lt;/span&gt;&lt;span class="sh"&gt;"&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;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;AI is no longer controlled by companies with money. You don't need to send questions to a cloud server, pay for API calls, or wait for a company to give you access. You can run the model yourself on your computer. &lt;/p&gt;

&lt;p&gt;It's not perfect and fast, but it works. And it works on a laptop with 8GB RAM.&lt;/p&gt;

&lt;p&gt;A few years ago, running a 70B AI model was fantasy. You needed a data center. But now, you need a laptop. It's a power shift. &lt;/p&gt;

&lt;p&gt;AI is no longer just for the rich, it's for anyone with a computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/0xSojalSec/airllm" rel="noopener noreferrer"&gt;AirLLM GitHub&lt;/a&gt; — The main tool&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://huggingface.co/meta-llama/Llama-2-70b-hf" rel="noopener noreferrer"&gt;LLaMA 2 70B on Hugging Face&lt;/a&gt; — Download the model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: Edited with AI Assistance&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>airllm</category>
      <category>ai</category>
      <category>github</category>
    </item>
    <item>
      <title>AI Created Its First Real Cyber Attack And It Bypassed 2FA</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Thu, 11 Jun 2026 21:46:05 +0000</pubDate>
      <link>https://dev.to/shresthapandey/ai-created-its-first-real-cyber-attack-and-it-bypassed-2fa-2lf2</link>
      <guid>https://dev.to/shresthapandey/ai-created-its-first-real-cyber-attack-and-it-bypassed-2fa-2lf2</guid>
      <description>&lt;p&gt;For the first time, AI has been used to exploit a software vulnerability. Google discovered it in May 2026, and the consequences could be serious.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Moment Everything Changed
&lt;/h3&gt;

&lt;p&gt;In May 2026, Google’s Threat Intelligence Group identified something that security  researchers had been warning about for years, and it came sooner than expected.&lt;/p&gt;

&lt;p&gt;Hackers used an AI model to create a working zero-day exploit, a cyber attack that targets a vulnerability no one knows about yet. And it wasn’t a simple attack. It bypassed two-factor authentication (2FA), the security layer that millions of people trust every single day.&lt;/p&gt;

&lt;p&gt;This is the first case of AI weaponizing a vulnerability for real-world attacks. Before this, zero-days required elite hackers and months of research. But now, AI can find them in hours.&lt;/p&gt;

&lt;p&gt;Let’s breakdown what happened and why it’s so terrifying.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Got Attacked?
&lt;/h3&gt;

&lt;p&gt;The target of attack was an open-source web-based system administration tool. Google didn’t disclose the name, but it’s a popular system that IT companies used to manage servers, websites and computers. &lt;/p&gt;

&lt;p&gt;The exploit let attackers log in without entering the second authentication code, even when 2FA was turned on. You could enter the password and the system would log you in directly, skipping the phone code setup completely.&lt;/p&gt;

&lt;p&gt;The vulnerability was not just some coding mistake like memory error, it was a logic flaw.&lt;/p&gt;

&lt;p&gt;The developers hardcoded something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_is_admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;skip_2fa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The traditional security scanners missed it, but AI found it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the Attack Worked?
&lt;/h3&gt;

&lt;p&gt;The AI used by hackers, wrote some Python script. &lt;/p&gt;

&lt;p&gt;The script still needed valid credentials to work, so knowing the correct password was required. But as soon as it had these credentials, it was able to bypass 2FA entirely. There was no phone code or verification required, and it went straight into the account.&lt;/p&gt;

&lt;p&gt;The major plan was mass exploitation. The cybercrime group wanted to use this on thousands of users of the tool at once. There goal was going after everyone using the software, not just one company.&lt;/p&gt;

&lt;p&gt;Google worked with the vendor to fix the vulnerability so the mass exploitation didn’t happen. But the exploit was real and AI created it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The AI Tells: How We Know It Was AI
&lt;/h3&gt;

&lt;p&gt;This was not just an assumption, the code had clear AI fingerprints.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Overly Explanatory Comments: The script had comments that explained what every function did, like a teacher walking through code. Human hackers don't write comments like that, but AI does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Hallucinated CVSS Score: The code had a CVSS score, that is, a security vulnerability rating, but the score didn't really exist, AI made it up.This was a hallucination, a common AI mistake.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Textbook Python Format: The code was perfectly clean without any hacker shortcuts or any optimizations. It was like a textbook Python from AI training data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Junk&amp;nbsp;&lt;code&gt;_C&lt;/code&gt;&amp;nbsp;Color Class: The script used a basic ANSI color class named&amp;nbsp;&lt;code&gt;_C&lt;/code&gt;. It's a common pattern in AI-generated code. Google found this exact pattern in multiple AI scripts.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A human hacker wouldn't include these points. They're too neat, too educational, too obvious, which indicates AI does this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why AI Found This Flaw
&lt;/h3&gt;

&lt;p&gt;Zero-day vulnerabilities require elite security talent. It actually needs someone who can read code, understand intent, and spot contradictions, which is hard.&lt;/p&gt;

&lt;p&gt;Modern large language models (LLMs) have something called context reasoning. They can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read through thousands of lines of code&lt;/li&gt;
&lt;li&gt;Understand what the developer was trying to do&lt;/li&gt;
&lt;li&gt;Find contradictions between intent and implementation&lt;/li&gt;
&lt;li&gt;Surface logic errors that look correct but are broken &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The zero-day was a high-level logic flaw. Security scanners don’t catch these. They look for memory errors, syntax mistakes, known vulnerability patterns, but they don’t understand developer intent.&lt;/p&gt;

&lt;p&gt;Google said frontier LLMs (the biggest AI models) are getting better at this. They can spot logic errors the way a senior security engineer would. But they do it faster, and they don’t get tired.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Changes Everything
&lt;/h3&gt;

&lt;p&gt;The scary part is, this will happen more.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Zero-Days are now scalable: Before AI, finding a zero-day was skilled work but now, AI can scan code, find logic flaws, and write exploits in hours.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2FA is not 100% safe: Two-factor authentication is the gold standard for security. It’s what you tell your employees to use. It’s what banking apps require to keep accounts safe. This exploit bypassed it. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI can find flaws humans miss: Traditional security tools look for patterns, check for known vulnerabilities, but they don’t understand intent. AI understands intent, reads code like a human would and finds contradictions, that are the most dangerous vulnerabilities.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What Developers Should Do Right Now
&lt;/h3&gt;

&lt;p&gt;You can’t stop AI from finding vulnerabilities, but you can make yourself a tougher target.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Update Your Packages Faster: When a vulnerability is announced, patch it as soon as you can. Attackers are moving faster with AI, so waiting weeks to update is a risk. If there’s a fix, apply it quickly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Audit Your Dependencies: Check what libraries and tools you're using. Are they popular? Do they have security teams? Are they open-source? Audit your entire dependency tree. Each one is a potential attack vector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't Trust AI Code 100%: If you use AI to write code, always review it. AI can get things wrong, add insecure code, or suggest solutions that don’t really work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get Ready for More AI-Powered Attacks: AI-driven attacks aren’t going away. Set up monitoring, watch for unusual activity, rotate passwords and keys regularly, and have a plan for handling security incidents.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;The AI-created zero-day is a turning point. It shows that AI is no longer just a future threat, it's already being used in real attacks.&lt;/p&gt;

&lt;p&gt;2FA is still a strong layer of security, but it’s not perfect. Logic flaws are still dangerous, and now AI can help find them faster. Zero-days are still uncommon, but creating them is becoming easier.&lt;/p&gt;

&lt;p&gt;This is just the beginning.&lt;/p&gt;

&lt;p&gt;The next thing could be much bigger.&lt;/p&gt;

</description>
      <category>zerodayexploit</category>
      <category>ai</category>
      <category>vickybytes</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>I Fine-Tuned Llama 3.2 on My Own Writing Style Using LoRA, Unsloth, and a Free Colab GPU</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Sun, 07 Jun 2026 14:38:38 +0000</pubDate>
      <link>https://dev.to/shresthapandey/i-fine-tuned-llama-32-on-my-own-writing-style-using-lora-unsloth-and-a-free-colab-gpu-13l5</link>
      <guid>https://dev.to/shresthapandey/i-fine-tuned-llama-32-on-my-own-writing-style-using-lora-unsloth-and-a-free-colab-gpu-13l5</guid>
      <description>&lt;p&gt;For a long time, fine-tuning language models felt like something that multiple people talked about, but very few actually tried themselves.&lt;/p&gt;

&lt;p&gt;Whenever I saw posts about fine-tuning, I saw things like massive datasets, GPU setups, and much more, which made me realise it’s out of reach. But it got added to my list of things to explore.&lt;/p&gt;

&lt;p&gt;Recently, while browsing Creator Labs on &lt;a href="https://vickybytes.com/creator-labs" rel="noopener noreferrer"&gt;VickyBytes&lt;/a&gt;, I came across one of those labs which sounded simple yet practical to someone like me, who wants to explore. It was: taking a small language model and fine-tune it on your own writing style. The main goal here was, Could a model learn to write more like me?&lt;/p&gt;

&lt;p&gt;As someone who spends a lot of time creating technical content, that question caught my attention.&lt;/p&gt;

&lt;p&gt;I assumed fine-tuning still needed expensive hardware, large datasets, and a significant amount of machine learning knowledge. Instead, I tried to lower the barrier and discovered tools like using Google Colab notebook, LoRA, and a small dataset built from my own content. &lt;/p&gt;

&lt;p&gt;This article documents the entire process from start to finish, including environment setup, dataset preparation, LoRA fine-tuning, evaluation, GGUF conversion, and the lessons I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does "Training on Your Writing Style" Actually Mean?
&lt;/h2&gt;

&lt;p&gt;A language model doesn’t understand who you are. It learns the statistical patterns present in your writing.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you prefer short or long paragraphs?&lt;/li&gt;
&lt;li&gt;Do you use analogies?&lt;/li&gt;
&lt;li&gt;Do you use emojis in your writing?&lt;/li&gt;
&lt;li&gt;Do you end posts with questions?&lt;/li&gt;
&lt;li&gt;Do you write formally or conversationally?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When enough examples are provided, the model begins reproducing those patterns.&lt;/p&gt;

&lt;p&gt;Thus, fine-tuning on writing style is less about teaching a model who you are and more about teaching it how you tend to communicate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture 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%2Fea6rg4wyn5s6ubwn6zky.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%2Fea6rg4wyn5s6ubwn6zky.png" alt="Architecture diagram" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a Model
&lt;/h2&gt;

&lt;p&gt;The lab suggested working with models between 1B and 7B parameters. Initially, I considered using one of the newer Qwen models. However, after exploring the available Unsloth notebooks, I decided to use:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Llama 3.2 3B Instruct&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Three reasons for this decision were:&lt;/p&gt;

&lt;p&gt;First, the model was small enough to fine-tune comfortably on free Colab resources. Second, Unsloth provides a mature training notebook for Llama models. Third, the resulting model can easily be exported to GGUF and run locally through Ollama.&lt;/p&gt;

&lt;p&gt;At this point, I wanted a model that could actually learn from a relatively small dataset and allow me to complete the entire worlflow on consumer-grade hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Environment
&lt;/h2&gt;

&lt;p&gt;I wanted the entire project to work on free resources. So instead of renting a GPU or using paid cloud infrastructure, I used Google Colab because it provides access to a free NVIDIA T4 GPU, which is sufficient for LoRA fine-tuning small language models such as Llama 3.2 3B.&lt;/p&gt;

&lt;p&gt;The first step was GPU acceleration.&lt;/p&gt;

&lt;p&gt;From the Colab runtime settings, I selected a GPU runtime as T4 GPU.&lt;/p&gt;

&lt;p&gt;Once the runtime was ready, I opened the official Unsloth notebook for Llama 3.2 and executed the setup cells.&lt;/p&gt;

&lt;p&gt;Unsloth handles much of the optimization automatically, which means there is very little configuration required from the user.&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%2Fcqq9mhpqkxljzkxg45qg.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%2Fcqq9mhpqkxljzkxg45qg.png" alt="T4 GPU" width="692" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Building and Preparing the Dataset&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This was the most important step in the entire project.&lt;/p&gt;

&lt;p&gt;Most of us think focus on discussing models instead of discussing more about the data. I experienced that building the dataset was more challenging as the result depends on the data we provide.&lt;/p&gt;

&lt;p&gt;Since the goal was to teach the model my writing style, I built the dataset using content I had already written over time, including LinkedIn posts, Instagram captions, technical explanations, and educational content.  I focused on examples that showed how I naturally write and explain technical concepts.&lt;/p&gt;

&lt;p&gt;One challenge I encountered was preparing the data in the format expected by the training pipeline. So, I converted each example into an instruction-response pair.&lt;/p&gt;

&lt;p&gt;Example:&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;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Write a LinkedIn post about Kubernetes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Kubernetes is one of those technologies..."&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;The notebook expected a JSONL dataset, where each example is stored as a separate JSON object. Initially, I spent some time on dataset loading errors before realizing the issue was with the structure of the data. To simplify, I stored the examples as a Python string inside the notebook and generated the &lt;code&gt;dataset.jsonl&lt;/code&gt; file before loading it.&lt;/p&gt;

&lt;p&gt;Once the dataset was formatted correctly, it loaded successfully and became the foundation for the rest of the fine-tuning process. It looked simple but it was one of the most important parts of the entire project because the model can only learn the patterns that exist in the data it receives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading the Dataset
&lt;/h2&gt;

&lt;p&gt;After correcting the JSONL structure, loading the dataset became simple.&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="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;data_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dataset.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train&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;To verify everything loaded correctly:&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rows:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preparing the Dataset for Llama 3.2
&lt;/h2&gt;

&lt;p&gt;The raw dataset still wasn’t ready for training because Llama expeects conversational data rather than instruction-response pairs.&lt;/p&gt;

&lt;p&gt;To solve this, I used Unsloth’s chat template utilities.&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.chat_templates&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_chat_template&lt;/span&gt;

&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_chat_template&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;chat_template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama-3.1&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 converts the dataset into the same format used by Llama during instruction tuning.&lt;/p&gt;

&lt;p&gt;A single training example now looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;|start_header_id|&amp;gt;user
Write a LinkedIn post about Docker

&amp;lt;|start_header_id|&amp;gt;assistant
Docker revolutionized...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, the model finally has data in a format it understands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fine-Tuning with Unsloth
&lt;/h2&gt;

&lt;p&gt;Once the dataset was completely ready, the training process became surprisingly easy.&lt;/p&gt;

&lt;p&gt;Using Unsloth’s SFTrainer, I configured LoRA training and launched the run.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;trainer.train()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;During training, the model repeatedly sees examples from the dataset and adjusts the LoRA adapter weights to better reproduce the expected responses. The original Llama weights remain frozen throughout the process.&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%2Fou2lrvymdtu5fnl5vcug.jpeg" 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%2Fou2lrvymdtu5fnl5vcug.jpeg" alt="LoRA fine-tuning of Llama 3.2 3B on a custom writing-style dataset using Unsloth" width="800" height="180"&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%2Fw6d4byvci3b1nzzatobz.jpeg" 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%2Fw6d4byvci3b1nzzatobz.jpeg" alt="Fine-tuning progress showing training loss decreasing over time" width="774" height="1055"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Did the Model Actually Learn My Writing Style?
&lt;/h2&gt;

&lt;p&gt;Now, after training, the main question was whether the model learned anything useful. To test this, I intentionally used prompts that were not included in the dataset.&lt;/p&gt;

&lt;p&gt;Examples included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt:
Explain LLMs in short

Generated Output:
Most AI today runs on a small change. Instead of building LLM itself, you give an AI model large amounts of text data and tell it to only remember things it decides on its own. This creates a system where the AI learns by responding to other people’s queries, and where it has to think for itself in order to provide an accurate answer. That’s a major pattern behind the AI today. Instead of fine-tuning a model that thinks like a dictionary and a research paper in one, you fine-tune a model that thinks like a person. And instead of giving it years of training data, you give it hundreds of thousands of your own pages that it will decide what to keep in memory on its own. That is the system most AI use today, and it has worked well for a lot of things.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output were not identical to my writing. That would actually be a bad sign because it would suggest memorization. The generated posts were more conversational, and more focused on helping readers understand concepts rather than simply describing them.&lt;/p&gt;

&lt;p&gt;That was the behavior I expected to achieve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding Overfitting
&lt;/h2&gt;

&lt;p&gt;One challenge with small datasets is overfitting.&lt;/p&gt;

&lt;p&gt;With only twenty examples, there is always a risk that the model memorizes responses instead of learning general writing patterns.&lt;/p&gt;

&lt;p&gt;To reduce that risk, I intentionally included multiple content formats and multiple technical topics. Using diverse topics helped push the model toward learning style patterns instead of topic-specific answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exporting the Model to GGUF
&lt;/h2&gt;

&lt;p&gt;After training completed, I expected to see a complete model. But LoRA only trains adapters. Those adapters must be merged with the original model before deployment.&lt;/p&gt;

&lt;p&gt;The workflow looks like this:&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%2Fx9g5ak05f63lblrjcr3x.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%2Fx9g5ak05f63lblrjcr3x.png" alt="GGUF Workflow" width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using Unsloth’s export utilities, I generated a GGUF version of the model using Q4_K_M quantization. This format is widely used by tools such as Ollama and llama.cpp.&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%2Fezcni0dddbj8s558svn3.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%2Fezcni0dddbj8s558svn3.png" alt="GGUF File" width="421" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Model Locally with Ollama
&lt;/h2&gt;

&lt;p&gt;One of the coolest part was realizing that the model no longer needed Colab. Once exported as GGUF, it could run locally.&lt;/p&gt;

&lt;p&gt;Using Ollama, the fine-tuned model can be loaded directly from a laptop without relying on external APIs.&lt;/p&gt;

&lt;p&gt;This means the writing-style model becomes self-hosted. The same personalized behavior learned during fine-tuning can now be accessed locally whenever needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;p&gt;Since this project involved training on personal content, it’s worth discussing about the privacy. I only used the content that I personally wrote and was comfortable using for experimentation.&lt;/p&gt;

&lt;p&gt;I avoided using private messages, confidential conversations, or any data that could create privacy concerns.&lt;/p&gt;

&lt;p&gt;The ability to train models on personal data is powerful, but consent and security should always be considered before building such AI systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;While working on this project, I learnt a few things.&lt;/p&gt;

&lt;p&gt;The most challenging parts were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building a useful dataset&lt;/li&gt;
&lt;li&gt;Correctly formatting JSONL files&lt;/li&gt;
&lt;li&gt;Understanding chat templates&lt;/li&gt;
&lt;li&gt;Understanding LoRA adapters&lt;/li&gt;
&lt;li&gt;Evaluating whether the model genuinely learned anything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual training process ended up being the easiest step. Modern tools such as Unsloth have simplified fine-tuning workflows. So the actual bottleneck is data quality, not just the hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Before starting this project, fine-tuning felt like something made only for machine learning engineers.&lt;/p&gt;

&lt;p&gt;After completing it, I think every developer should try it once.&lt;/p&gt;

&lt;p&gt;Something that surprised me was how strongly the dataset influenced the final results. Even with a small collection of examples, the model began reproducing many of the patterns that consistently appear in my writing.&lt;/p&gt;

&lt;p&gt;And after spending several days building, debugging, training, evaluating, and exporting the model, I’m convinced that the quality of the examples matters a lot.&lt;/p&gt;

&lt;p&gt;Would I use this model for real content creation? Probably not yet. The dataset is still too small, and the outputs occasionally differ from my writing style. &lt;/p&gt;

&lt;p&gt;However, the experiment successfully demonstrated that modern fine-tuning tools have lowered the barrier significantly. Something that once felt like a machine learning research project can now be completed on a free Colab GPU over a week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Fine-Tuning Framework: &lt;a href="https://github.com/unslothai/unsloth" rel="noopener noreferrer"&gt;Unsloth&lt;/a&gt;&lt;br&gt;
Model Notebooks: &lt;a href="https://unsloth.ai/docs/get-started/unsloth-notebooks#standard-sft-notebooks" rel="noopener noreferrer"&gt;Unsloth Notebook&lt;/a&gt;&lt;br&gt;
Running Models Locally: &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;br&gt;
GitHub Repository: &lt;a href="https://github.com/Shresthap21/Fine-tuning-LLM-to-write-in-your-own-style" rel="noopener noreferrer"&gt;Fine-tuning-LLM-to-write-in-your-own-style&lt;/a&gt;&lt;br&gt;
Lab Inspiration: &lt;a href="https://vickybytes.com/creator-labs" rel="noopener noreferrer"&gt;VickyBytes Creator Labs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>ollama</category>
      <category>ai</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
