<?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>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>
    <item>
      <title>I Built a GitHub Bot That Reviews My Pull Requests Using a Local LLM</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Wed, 20 May 2026 09:46:21 +0000</pubDate>
      <link>https://dev.to/shresthapandey/i-built-a-github-bot-that-reviews-my-pull-requests-using-a-local-llm-1f6l</link>
      <guid>https://dev.to/shresthapandey/i-built-a-github-bot-that-reviews-my-pull-requests-using-a-local-llm-1f6l</guid>
      <description>&lt;p&gt;Last month I pushed a bug to production which I would have caught if I had somebody look at my code. Well, it's not like I was the only one working on the project, so I had to read it myself and then forget about it, only to spend an annoying Tuesday trying to figure out why it didn't ship.&lt;/p&gt;

&lt;p&gt;I was thinking about Ollama. I had heard about it a couple of times but never really needed to use it for anything. So this weekend I built a GitHub bot that reviews pull requests with a local LLM, without sending any code out of the box. It makes a comment on the PR with what it finds.&lt;/p&gt;

&lt;p&gt;Here's how I built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the bot actually does
&lt;/h2&gt;

&lt;p&gt;When you open a pull request, GitHub fires a webhook. The bot receives it, pulls the diff for each changed file using GitHub's REST API, sends that diff to a locally running LLM via Ollama, and posts the review back as a comment on the PR.&lt;/p&gt;

&lt;p&gt;The output looks like this on your PR:&lt;/p&gt;

&lt;p&gt;Project Structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;self-hosted-ai-code-review-bot-for-github-prs/
├── src/
│   ├── server.js
│   ├── github.js
│   ├── ollama.js
│   ├── diffParser.js
│   └── chunker.js
├── private-key.pem
├── .env
├── package-lock.json
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up the GitHub App first
&lt;/h2&gt;

&lt;p&gt;Before any code, you need a GitHub App. A GitHub App has its own identity, which gets installed on specific repos, and uses short-lived installation tokens instead of long-lived credentials.&lt;/p&gt;

&lt;p&gt;Go to GitHub → Settings → Developer Settings → GitHub Apps → New GitHub App.&lt;/p&gt;

&lt;p&gt;Permissions you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull requests: Read &amp;amp; write&lt;/li&gt;
&lt;li&gt;Issues: Read &amp;amp; write&lt;/li&gt;
&lt;li&gt;Metadata: Read-only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under webhook events, subscribe to Pull request. Set the webhook URL to your ngrok address + /webhook (we'll come back to that).&lt;br&gt;
Generate and download the private key (.pem file). GitHub uses this to sign the installation tokens.&lt;/p&gt;

&lt;p&gt;Clone the repo and install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &amp;lt;your-repo-url&amp;gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;repo-name&amp;gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;PORT&lt;/span&gt;=&lt;span class="m"&gt;3000&lt;/span&gt;
&lt;span class="n"&gt;GITHUB_APP_ID&lt;/span&gt;=&lt;span class="n"&gt;your_app_id&lt;/span&gt;
&lt;span class="n"&gt;GITHUB_PRIVATE_KEY_PATH&lt;/span&gt;=./&lt;span class="n"&gt;privatekey&lt;/span&gt;.&lt;span class="n"&gt;pem&lt;/span&gt;
&lt;span class="n"&gt;WEBHOOK_SECRET&lt;/span&gt;=&lt;span class="n"&gt;your_webhook_secret&lt;/span&gt;
&lt;span class="n"&gt;OLLAMA_MODEL&lt;/span&gt;=&lt;span class="n"&gt;deepseek&lt;/span&gt;-&lt;span class="n"&gt;coder&lt;/span&gt; &lt;span class="c"&gt;# or llama3, whichever you're running
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install the app on your repository via the Install App option.&lt;/p&gt;

&lt;h3&gt;
  
  
  github.js — auth and API calls
&lt;/h3&gt;

&lt;p&gt;GitHub Apps don't use static tokens. You sign a JWT with your private key, exchange it for a short-lived installation token, and use that to make API calls. &lt;code&gt;@octokit/auth-app&lt;/code&gt; handles all of this:&lt;/p&gt;

&lt;h2&gt;
  
  
  The auth layer
&lt;/h2&gt;

&lt;p&gt;GitHub Apps use installation tokens, not static keys. You sign a JWT with your private key, exchange it for a short-lived token, use that token for API calls. &lt;code&gt;@octokit/auth-app&lt;/code&gt; handles the whole flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getOctokit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;installationId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;privateKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_PRIVATE_KEY_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAppAuth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;privateKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;installationId&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;installationAuth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;installation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Octokit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;installationAuth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&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;You pass the &lt;code&gt;installationId&lt;/code&gt; from the webhook payload. GitHub includes it in every event so you always know which installation triggered it.&lt;br&gt;
The other two functions in &lt;code&gt;github.js&lt;/code&gt; are straightforward: one calls &lt;code&gt;octokit.pulls.listFiles&lt;/code&gt; to get the changed files with their diffs, the other calls &lt;code&gt;octokit.issues.createComment&lt;/code&gt; to post the final review.&lt;/p&gt;
&lt;h2&gt;
  
  
  Filtering and chunking the diff
&lt;/h2&gt;

&lt;p&gt;Not every file in a PR deserves review time. &lt;code&gt;package-lock.json&lt;/code&gt;, &lt;code&gt;.vscode/&lt;/code&gt; settings, minified files, we skip them. GitHub also sometimes returns files without a patch field (binary files, files too large to diff), those get skipped too.&lt;/p&gt;

&lt;p&gt;For files that do make it through, large diffs need to be split before sending to the LLM. Context windows are limited and model quality drops toward the end of long inputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;chunkDiff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chunks&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;1500 characters per chunk works reliably with both Llama 3 and DeepSeek Coder. Each chunk gets its own Ollama call, results get concatenated per file.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompt is important
&lt;/h2&gt;

&lt;p&gt;This is where I spent the most time, and where the whole thing either works or doesn't.&lt;/p&gt;

&lt;p&gt;My first version was something unclear like "review this diff and point out issues." The output was not good. The model would write paragraphs explaining what the file does, invent security vulnerabilities that weren't in the code, reference issue numbers that don't exist, and end with generic advice like "make sure to add proper error handling throughout your application." &lt;/p&gt;

&lt;p&gt;The problem is that without constraints, the model tries to be helpful in every direction at once. It doesn't know you only care about what changed. So you have to be as explicit about what you don't want as what you do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reviewWithOllama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diffChunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
You are an AI GitHub pull request reviewer.
You are reviewing ONLY the provided git diff.

STRICT RULES:
- Review ONLY changed lines from the diff.
- Ignore unchanged code completely.
- Do NOT explain the whole application.
- Do NOT give generic software engineering advice.
- Do NOT hallucinate missing features.
- Do NOT invent security issues.
- Do NOT invent issue IDs, ticket numbers, PR references, or metadata.
- Do NOT mention issues outside the diff.
- Every issue MUST directly relate to a changed line.
- Keep response concise.
- If no real issue exists, reply exactly: "No significant issues found."

Focus ONLY on:
1. Potential bugs introduced
2. Style or readability issues introduced
3. Performance issues introduced
4. Security risks directly introduced
5. Small improvement suggestions directly related to the diff

Diff:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;diffChunk&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:11434/api/generate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OLLAMA_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&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;Once I added the negative rules, the output became useful. The model stopped inventing context and started pointing at specific changed lines.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;temperature: 0&lt;/code&gt; — code review isn't creative work. You want the model to be deterministic, same diff producing consistent output. It also reduces hallucinations noticeably.&lt;/p&gt;

&lt;h2&gt;
  
  
  The webhook handler
&lt;/h2&gt;

&lt;p&gt;Express receives the event, checks if it's worth handling, and orchestrates everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-github-event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pull_request&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ignored&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opened&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;synchronize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reopened&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ignored action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;installationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;installation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pullNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;octokit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getOctokit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;installationId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPullRequestFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;octokit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pullNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;finalReview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`# 🤖 AI Code Review\n\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;shouldReviewFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chunkDiff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fileReview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fileReview&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;reviewWithOllama&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;finalReview&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`## File: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileReview&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;---\n\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;postReviewComment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;octokit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pullNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalReview&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Webhook error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error processing webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;p&gt;Install Ollama from &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;ollama.com&lt;/a&gt; and pull a model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run llama3
&lt;span class="c"&gt;# or, better for code&lt;/span&gt;
ollama run deepseek-coder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the server and expose it with ngrok:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
ngrok http 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the ngrok HTTPS URL, update your GitHub App's webhook URL to &lt;a href="https://your-ngrok-url/webhook" rel="noopener noreferrer"&gt;https://your-ngrok-url/webhook&lt;/a&gt;, push a branch, open a PR.&lt;/p&gt;

&lt;p&gt;CPU vs GPU: Depending on hardware, local inference on CPU may take several seconds per diff chunk. Llama 3 8B on a laptop CPU takes 10–20 seconds per diff chunk. For a background bot that's fine, you open the PR, switch tabs, comment shows up a minute later. With an NVIDIA GPU, Ollama picks it up automatically and it's under 2 seconds. &lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying it properly
&lt;/h2&gt;

&lt;p&gt;ngrok dies when you close your terminal. For anything persistent, a cheap VPS works. DigitalOcean's $6/month droplet, small EC2 instance, whatever. Install Node.js and Ollama on it, put Nginx in front with SSL, point your webhook URL at the domain.&lt;/p&gt;

&lt;p&gt;Tools like GitHub Copilot Code Review send your diff to external servers. With this setup the diff goes from GitHub's API to your own machine into your local model which means nothing leaves your infrastructure. For proprietary codebases that's not a minor thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the idea came from
&lt;/h2&gt;

&lt;p&gt;I came across &lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;VickyBytes&lt;/a&gt; a few weeks back. It's a platform that posts structured project ideas in Creator Labs, where you build something, write about it, and earn from it if the content is good enough. This was one of those.&lt;br&gt;
Having a concrete spec helped. "Play with Ollama" had been on my list for months and never happened. A specific project with a clear outcome actually got it done. The bot runs on my own repos now, so that worked out.&lt;br&gt;
If you write technical content and want structured project ideas to build around, visit &lt;a href="https://vickybytes.com" rel="noopener noreferrer"&gt;vickybytes.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Full source is on &lt;a href="https://github.com/Shresthap21/Self-Hosted-AI-Code-Review-Bot-for-GitHub-PRs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. The prompt is where most of the interesting experimentation happens. That's where you'll actually learn how local LLMs behave in practice.&lt;/p&gt;

</description>
      <category>vickybytes</category>
      <category>opensource</category>
      <category>ollama</category>
      <category>webhook</category>
    </item>
    <item>
      <title>Why Reasoning Models Changed Everything</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Fri, 10 Apr 2026 14:38:33 +0000</pubDate>
      <link>https://dev.to/shresthapandey/why-reasoning-models-changed-everything-5e1n</link>
      <guid>https://dev.to/shresthapandey/why-reasoning-models-changed-everything-5e1n</guid>
      <description>&lt;p&gt;&lt;em&gt;For years, making language models smarter meant making them bigger. Then someone asked a different question: what if, instead of training more, you let the model think longer?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In September 2024, OpenAI released o1. In January 2025, DeepSeek released R1. These two models together, invalidated the assumption that was governing the entire field since 2020,  that the path to better AI runs through bigger training runs.&lt;/p&gt;

&lt;p&gt;This assumption was backed by the Kaplan et al. scaling laws paper, which showed the language model performance follows a reliable power law with training compute. If you provide more parameters, more data, more GPU-hours, you get a more capable model. The field organized itself around this insight. Every major lab poured billions into pre-training. &lt;/p&gt;

&lt;p&gt;o1 and R1 showed that there’s a second dimension to scale that the field had largely ignored: compute at inference time. And it turns out that for tasks requiring multi-step reasoning, this second dimension can be just as powerful as the first, and far cheaper to exploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What chain-of-thought actually is
&lt;/h2&gt;

&lt;p&gt;Chain-of-thought prompting has been around since 2022, when Wei et al. at Google Brain showed that simply asking a language model to “think step by step” before answering dramatically improved its performance on math and logic tasks. This was a really surprising result. The model wasn’t being retrained, it was just prompted differently. The extra tokens the model generated while reasoning served as scratchpad that improved its final answer.&lt;/p&gt;

&lt;p&gt;Transformers generate one token at a time, and each token is conditioned on all previous tokens. When a model solves a math problem by writing out steps, those steps become part of the context that informs the final answer. The model is using its own output as working memory. Without chain-of-thought, it has to compress all that computation into a single forward pass.&lt;/p&gt;

&lt;p&gt;Nobody had figured out how to train a model to do this reliably, until o1. Prompting a model to “think step by step” helps, but the quality of reasoning is inconsistent. You might get careful and structured reasoning sometimes, but sometimes you get verbose filler that doesn’t really help. The model doesn’t know when to think hard and when not to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reinforcement learning connection
&lt;/h2&gt;

&lt;p&gt;OpenAI’s o1 system card describes training the model with reinforcement learning to produce a chain of thought before answering. The core thing is that RL can teach the model how to think specifically, to develop reasoning strategies that lead to correct answers on verifiable tasks like mathematics and code.&lt;/p&gt;

&lt;p&gt;According to OpenAI’s published description, o1 learns through RL to recognize and correct its mistakes in the middle of reasoning, break hard problems into simpler subproblems, and abandon approaches that aren’t working. These behaviors like self-correction, decomposition, backtracking, emerge from the training signal.&lt;/p&gt;

&lt;p&gt;The result is a model where more thinking time produces better answers. On the AIME 2024 benchmark (American Invitational Mathematics Examination), o1 scored in the 74th percentile of human test-takers. GPT-4o, using the same benchmark, scored around 9%. That gap is from the model being trained to use its inference compute more productively.&lt;/p&gt;

&lt;h2&gt;
  
  
  DeepSeek R1: showing the mechanism
&lt;/h2&gt;

&lt;p&gt;DeepSeek R1 published in January 2025 was an extremely important advancement because it achieved on benchmark scores the same level of performance as o1; however, this is the easy part. The significance of DeepSeek was that it provided an in-depth description of the training recipe allowing other people in the field the ability to study and replicate it.&lt;/p&gt;

&lt;p&gt;They started with DeepSeek-V3-Base, a 671B parameter pretrained model built on a Mixture-of-Experts architecture. Their first experiment called DeepSeek-R1-Zero, applied reinforcement learning directly to this base model without supervised fine-tuning beforehand. The reward signal was such that, the model gets rewarded for producing a correct final answer, and for formatting its output with explicit reasoning inside &lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; tags. &lt;/p&gt;

&lt;p&gt;No direct human-labeled examples were used to provide the basis for generating a reward signal for successful attempts. Similarly, no preference-based human reward model was utilized for training and reward purposes for deep learning via reinforcement learning. Simply put, their reinforcement learning experiment used the following evaluation criteria: (1) Did you provide the correct final answer?&lt;/p&gt;

&lt;p&gt;The results of R1-Zero were remarkable and, to be honest, a little unsettling. The model's average pass@1 score on AIME 2024 increased from 15.6% at the start of training to 71.0% by the end. More striking was what happened to the model's behavior during this process. The reasoning traces grew substantially longer as training progressed. &lt;/p&gt;

&lt;p&gt;The model spontaneously developed strategies like re-reading the problem from the beginning partway through a solution, checking its own work, and explicitly noting when it suspected an error. None of this was designed in. It emerged from optimizing for correct answers.&lt;/p&gt;

&lt;p&gt;The paper describes a notable emergent behavior where the model, while solving a math problem, pauses mid-reasoning, re-evaluates its approach, and switches to a different strategy to reach the correct answer. This emerges from reward training, where the model learns that revisiting its reasoning can sometimes lead to better outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The training algorithm: GRPO&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The RL algorithm that DeepSeek used (Group Relative Policy Optimization (GRPO)),  is worth understanding, because it's part of why this approach is tractable at scale.&lt;/p&gt;

&lt;p&gt;Standard reinforcement learning for language models (the approach used in earlier RLHF pipelines) depends on Proximal Policy Optimization (PPO). This requires an additional network called a critic, which is basically another large neural network that can estimate the value of partially completed responses. The size of the critic is typically the same as the size of the model that is being trained, resulting in twice the memory/compute requirements of the PPO-based reinforcement learning algorithm, because you must perform two full forward passes for every training step.&lt;/p&gt;

&lt;p&gt;GRPO, first introduced in the DeepSeekMath paper (2024), eliminates the critic. With GRPO, instead of estimating value through a learned value estimation model, GRPO now generates multiple responses for each prompt and receives a reward for each response. This group of multiple responses will provide an average reward, or baseline, for comparison when determining the advantage of any response (i.e., the signal that tells the model whether that particular response was better or worse than what was expected) through the formula:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;A_i = (r_i - mean(r_1...r_G)) / std(r_1...r_G)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is similar to the REINFORCE algorithm from the 90’s, however it is being created on larger scales with modern hardware and still holds the same clipping mechanism found within PPO for training stability. Because of this, you would see how using 50% fewer resources is huge, especially given that your policy model contains 671 billion parameters.&lt;/p&gt;

&lt;p&gt;There were only two main types of rewards that were given in the reward function: correctness rewards (whether or not the final answer matched the ground truth for math problems and/or coding problems) and format rewards (whether or not the model used the structure expected for output). &lt;/p&gt;

&lt;p&gt;There were no process rewards, nor step-by-step supervision. The implicit assumption of this approach was that if you give the model a correct-answer signal, along with a good deal of freedom to explore, the model will be able to find its own way to good reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why R1-Zero wasn't the final model
&lt;/h2&gt;

&lt;p&gt;R1-Zero had a problem because DeepSeek-V3-Base was pretrained on multilingual data, the model sometimes switched languages mid-reasoning. It would start a problem in English, shift into Chinese for a few sentences then continue in English. The reasoning was often correct but the output was unreadable. It also had readability issues. The thinking traces were sometimes clear internal monologues, but at other times, they were nearly incoherent.&lt;/p&gt;

&lt;p&gt;DeepSeek R1 addressed this through a more involved training pipeline. They first collected a small number of high-quality chain-of-thought examples that demonstrated the kind of structured and readable reasoning they wanted. This was used for supervised fine-tuning before RL began, giving the RL process a better starting point. They also added a language consistency reward to penalize mid-reasoning language switching. A subsequent round of rejection sampling and SFT on the RL model's own outputs added coverage for non-reasoning tasks like writing and general question-answering. The result DeepSeek-R1, performs comparably to OpenAI o1-1217 on reasoning benchmarks.&lt;/p&gt;

&lt;p&gt;DeepSeek released smaller models like 7B, 14B, 32B parameters, trained by fine-tuning on reasoning traces generated by the full R1 model. The 32B distilled version outperforms o1-mini on several benchmarks. This is knowledge distillation applied to reasoning i.e, smaller models learn reasoning patterns from larger models, without running the expensive RL training themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling just got a second dimension
&lt;/h2&gt;

&lt;p&gt;The pre-training scaling laws described by Kaplan et al. in 2020 showed a clean relationship, i.e, training compute in, model capability out. The Chinchilla paper (Hoffmann et al., 2022) refined this further, showing that for a fixed compute budget, the optimal strategy is to train a smaller model on more data rather than a larger model on less data. These results organized the field for years.&lt;/p&gt;

&lt;p&gt;Reasoning models introduce a second scaling curve. OpenAI’s o1 blog says performance improves when the model gets more time to think. &lt;/p&gt;

&lt;p&gt;In “The Bitter Lesson” (2019), Richard Sutton makes the case that methods of making use of computing power in a general sense usually outperform in the long term methods that encode human knowledge explicitly. The first instance of this was the scaling of the amount of computational resources utilized for pre-training; the second instance is the scaling of the number of computational resources utilized for inference through the use of learned reasoning. This indicates that we are not done with increasing the scale of AI; in actuality, we have only just entered into a new chapter in that story.&lt;/p&gt;

&lt;p&gt;That being said, scaling with regards to inference does work best for tasks that can have verified answers (i.e., math, formal proofs, code that can be tested). This makes it rather easy to determine whether the answer produced by a model is correct or not (there is either an answer or there is not). &lt;/p&gt;

&lt;p&gt;Extending this to tasks that are open ended (e.g., writing) where there is no clear ground truth is a very active area of research and far more difficult. The current generation of reasoning models is likely very powerful in the STEM and coding fields, although exactly how broadly they will be able to extend beyond these fields remains an active area of research as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  This one is different
&lt;/h2&gt;

&lt;p&gt;There is often a claim every few years that AI has reached a major breakthrough, but it usually turns out to be a small improvement. But this time is different. Now models can use more compute during inference to produce better reasoning, which creates new possibilities that were not available before.&lt;/p&gt;

&lt;p&gt;Tasks that previously needed human intervention because models can’t be trusted to reason carefully, now work differently. You can spend more compute at inference time to get more reliable results. For simple tasks the model can run fast, while harder or more important problems can be given more time to think, making the tool much more flexible than models from a few years ago. &lt;/p&gt;

&lt;p&gt;DeepSeek R1 being released with open weights under an MIT license also changes the economics. The distilled R1-32B model, when used with quantization on a single high-end GPU, performs better than o1-mini on numerical tests. Now that there are multiple APIs, labs, researchers, and smaller organizations with limited resources can take advantage of them. This is a major change in the number of people who would have had access to this type of model before the DeepSeek R1 was made freely available.&lt;/p&gt;

&lt;p&gt;Making models bigger and training them on more data helps them learn more, like reading lots of books makes someone more knowledgeable. But letting models “think longer” when answering questions helps them reason better and make fewer mistakes, like a person taking more time to work through a problem.&lt;/p&gt;

&lt;p&gt;Both of these improvements are important and now we have both. What we do with that combination is still an open question.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2501.12948" rel="noopener noreferrer"&gt;DeepSeek-R1 Paper&lt;/a&gt; — DeepSeek-AI, 2025&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openai.com/index/learning-to-reason-with-llms" rel="noopener noreferrer"&gt;Learning to Reason with LLMs&lt;/a&gt; — OpenAI, 2024&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://arxiv.org/abs/2402.03300" rel="noopener noreferrer"&gt;DeepSeekMath / GRPO Paper&lt;/a&gt; — Shao et al., 2024&lt;/li&gt;
&lt;/ul&gt;

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

</description>
      <category>vickybytes</category>
      <category>machinelearning</category>
      <category>deeplearning</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why Terraform Breaks After Day-1 And How Terraform Actions Fix It</title>
      <dc:creator>Shrestha Pandey</dc:creator>
      <pubDate>Wed, 01 Apr 2026 08:00:42 +0000</pubDate>
      <link>https://dev.to/shresthapandey/why-terraform-breaks-after-day-1-and-how-terraform-actions-fix-it-435c</link>
      <guid>https://dev.to/shresthapandey/why-terraform-breaks-after-day-1-and-how-terraform-actions-fix-it-435c</guid>
      <description>&lt;p&gt;Let me start with something most infrastructure engineers might not say out loud — Terraform solves Day-1 beautifully and then kinda leaves you hanging.&lt;/p&gt;

&lt;p&gt;You write your HCL, run &lt;code&gt;terraform apply&lt;/code&gt;, and everything is provisioned perfectly. The state file appears impeccable. But six months later that same infrastructure has been poked, patched, manually changed and silently drifted away from what terraform thinks exists. No one realizes this until something breaks in production.&lt;/p&gt;

&lt;p&gt;This article is about that “gap” between provisioning and actually managing infrastructure across its entire lifetime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day-2 Is Where Infrastructure Goes to Die (Slowly)
&lt;/h2&gt;

&lt;p&gt;When a full stack is provisioned onto AWS using Terraform it has a good state and everything is the same, and then after some time passes and a deployment fails, someone logs into the console and changes a security group rule; now the deployment has been successful… but this change has not been documented and no tickets have been raised regarding this change.&lt;/p&gt;

&lt;p&gt;When they run the scheduled &lt;code&gt;terraform apply&lt;/code&gt;, Terraform sees the difference and resets the security group to the original state, resulting in production breaking. Everyone is confused because there were no code changes made.&lt;/p&gt;

&lt;p&gt;The root cause of this issue is that the tools have not been designed for such usage; Terraform's core capability was to provide an infrastructure provisioning capability.&lt;/p&gt;

&lt;p&gt;Therefore, what are teams doing for their Day-2 operations? Most have a combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash scripts that contain parts nobody understands&lt;/li&gt;
&lt;li&gt;AWS Console changes that are made manually and never documented&lt;/li&gt;
&lt;li&gt;Ad-hoc Ansible runs that don't tie back to Terraform state in any way&lt;/li&gt;
&lt;li&gt;Lambda functions that are each triggering another Lambda function creating a non-traceable chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In total, over 30 different tools are managing a single hybrid infrastructure estate, which is being actively managed by organizations in the field.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lifecycle Nobody Talks About Enough
&lt;/h2&gt;

&lt;p&gt;Infrastructure has four phases and most of the industry focuses heavily over two of them.&lt;/p&gt;

&lt;p&gt;The first phase, or "Day-0", is the "Build Phase." In this phase an organisation will form their infrastructure and define policies. There has not been any provisioning yet and is done in partnership with the platform and security teams.&lt;/p&gt;

&lt;p&gt;The second phase, or "Day-1", is "Deploy Phase." In this phase &lt;code&gt;terraform apply&lt;/code&gt; will run, infrastructure will be built, and the application teams will deploy their workloads. This is where terraform really starts to show its capabilities.&lt;/p&gt;

&lt;p&gt;Day-2 or "Manage Phase." This phase is where management happens, patches are installed, configurations are changed, certificates are renewed and scaled as needed and where compliance is checked for validity and accuracy. Day 2 can take years to complete and it is also were all of the operational pain will occur. Terraform traditionally has no place in this phase.&lt;/p&gt;

&lt;p&gt;Day-N "Decommission Phase." This phase is where everything is removed and cleaned up.&lt;/p&gt;

&lt;p&gt;Over the last ten years the DevOps industry has been focused on perfecting Day-1 tooling; however, there are very few tools available for Day-2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform Actions — What Changed in v1.14
&lt;/h2&gt;

&lt;p&gt;Terraform Actions were added as stable functionality in Terraform v1.14 and were unveiled at HashiConf 2025. Now, providers can execute an action that does more than just CRUD - calling a lambda function, stopping an EC2, invalidating a CloudFront cache, or triggering an Ansible playbook.&lt;/p&gt;

&lt;p&gt;These new actions are located in their own top-level action block in your HCL. Terraform can automatically execute them based on event triggers during a resource's lifecycle, or they can be invoked manually via the CLI without the need to do a complete &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can invoke an operational action (such as calling a lambda to warm up a cache) without having Terraform re-evaluate the entire state of your infrastructure. This is a significant change in how you will use your infrastructure from now on.&lt;/p&gt;

&lt;p&gt;The AWS provider currently has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;aws_lambda_invoke&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aws_ec2_stop_instance&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aws_cloudfront_create_invalidation&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Actions Actually Work — The Syntax
&lt;/h2&gt;

&lt;p&gt;There are two pieces. The action block itself, and the trigger that fires it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining an Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_invoke"&lt;/span&gt; &lt;span class="s2"&gt;"warm_cache"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache_warmer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform_action"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;config {}&lt;/code&gt; wrapper. Provider-specific arguments go inside &lt;code&gt;config&lt;/code&gt;, not directly in the action block. &lt;/p&gt;

&lt;p&gt;Meta-arguments like &lt;code&gt;count&lt;/code&gt; and &lt;code&gt;provider&lt;/code&gt; exist outside &lt;code&gt;config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_invoke"&lt;/span&gt; &lt;span class="s2"&gt;"warm_cache"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoke_on_deploy&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;us_east_1&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache_warmer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform_action"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Triggering an Action on Resource Lifecycle Events
&lt;/h3&gt;

&lt;p&gt;This goes inside the resource's &lt;code&gt;lifecycle&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"api"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-api-handler"&lt;/span&gt;
  &lt;span class="c1"&gt;# ... rest of config&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warm_cache&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two main things to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;events&lt;/code&gt; uses unquoted keywords — &lt;code&gt;after_create&lt;/code&gt; and &lt;code&gt;after_update&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;actions&lt;/code&gt; is plural and takes a list, not a single reference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also add a &lt;code&gt;condition&lt;/code&gt; to guard the action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ansible_playbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patch_instance&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_auto_patching&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;When &lt;code&gt;condition&lt;/code&gt; is false, the action is skipped completely. This is useful when the configuration should exist but only run in certain environments, like production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Actions from the CLI
&lt;/h3&gt;

&lt;p&gt;This is where it gets useful for Day-2 workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Just plan the action, don't run it&lt;/span&gt;
terraform plan &lt;span class="nt"&gt;-invoke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;action.aws_lambda_invoke.warm_cache

&lt;span class="c"&gt;# Actually run the action&lt;/span&gt;
terraform apply &lt;span class="nt"&gt;-invoke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;action.aws_lambda_invoke.warm_cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform only executes that one action. No evaluation or change of any other part of your configuration occurs. Each action can only be executed once at a time; therefore, multiple &lt;code&gt;-invoke&lt;/code&gt; cannot be run in a single command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provisioning EC2 + Immediate Patching via Ansible Automation Platform
&lt;/h2&gt;

&lt;p&gt;One of the most important and widely used use cases is linking EC2 provisioning and automated patching through Ansible Automation Platform (AAP).&lt;/p&gt;

&lt;p&gt;The challenge it solves is simple; there are usually many security patches pending for an Ubuntu AMI that has been provisioned several months prior. If EC2 instances are provisioned, and then you manually take the time to patch each one independently, then at some point (most likely within 30 days) you will not patch an instance you provisioned. Thus, the solution is to link the patching process to the lifecycle of the Terraform instance provisioning so that patching cannot be missed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Terraform Side
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"instance_count"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu_ami"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AMI ID — use a recent Ubuntu LTS, patching will handle the rest"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aap_controller_url"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aap_oauth_token"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"allow_instance_reboot"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"app_servers"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_count&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ubuntu_ami&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.medium"&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_key_pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key_name&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow_ssh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app-server-${count.index}"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ansible_aap_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patch_servers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;after_update&lt;/code&gt; event is critical; should an instance be replaced (due to AMI update, instance type modification, or any reason that would force a new instance to be created), the patching will occur on the newly-created instance automatically without any manual intervention required.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Action Block
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"ansible_aap_job"&lt;/span&gt; &lt;span class="s2"&gt;"patch_servers"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controller_url&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aap_controller_url&lt;/span&gt;
    &lt;span class="nx"&gt;oauth_token&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aap_oauth_token&lt;/span&gt;
    &lt;span class="nx"&gt;job_template_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EC2 Linux Patching"&lt;/span&gt;
    &lt;span class="nx"&gt;extra_vars&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;vm_hosts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_servers&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;instance_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
          &lt;span class="nx"&gt;public_ip&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;allow_reboot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow_instance_reboot&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Credentials are stored in HCP Terraform's sensitive variable store. Instance IDs and IPs come straight from resource state at runtime, so AAP always gets current values.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You must refer to your provider documentation to verify the argument names of your AAP action based upon the version you are using, while the structure remains valid.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Ansible Playbook
&lt;/h3&gt;

&lt;p&gt;AAP receives &lt;code&gt;vm_hosts&lt;/code&gt; as an extra variable, builds inventory dynamically, and patches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Patch EC2 Instances&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;
  &lt;span class="na"&gt;gather_facts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

  &lt;span class="na"&gt;pre_tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait for SSH connectivity&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.wait_for_connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt;
        &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gather package facts&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.package_facts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apt&lt;/span&gt;

  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update apt package index&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;update_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
        &lt;span class="na"&gt;cache_valid_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apply security patches&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.apt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;upgrade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dist&lt;/span&gt;
        &lt;span class="na"&gt;only_upgrade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;patch_result&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check if reboot is required&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.stat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/reboot-required&lt;/span&gt;
      &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;reboot_required_file&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Reboot if needed and allowed&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.reboot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;reboot_timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
        &lt;span class="na"&gt;post_reboot_delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
      &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;reboot_required_file.stat.exists&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;allow_reboot | default(false) | bool&lt;/span&gt;

  &lt;span class="na"&gt;post_tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Verify instance is up after patching&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.ping&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/var/run/reboot-required&lt;/code&gt; is a file Ubuntu creates automatically when a package update (typically a kernel patch) requires a restart to take effect. The playbook checks for this file rather than blindly rebooting. And even then, it only reboots if &lt;code&gt;allow_reboot&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, which is controlled from your Terraform variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  AAP Job Template Configuration
&lt;/h3&gt;

&lt;p&gt;With respect to Ansible Automation Platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project&lt;/strong&gt; is a reference to the Git repo containing your playbook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt; is created dynamically from the &lt;code&gt;vm_hosts&lt;/code&gt; value which is assigned to you by Terraform when it runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials&lt;/strong&gt; - Your SSH Private RSA key will be stored in AAP's credential vault and will be used to connect to the VM via SSH. This is a very secure separation of the enterprise applications and their configurations (Terraform) and how to connect to them (Ansible).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the Full Workflow Looks Like
&lt;/h2&gt;

&lt;p&gt;An engineer modifies the &lt;code&gt;instance_count&lt;/code&gt; from 2 to 5 and then sends a Git commit with the modification.&lt;/p&gt;

&lt;p&gt;The engineer pushes it to HCP Terraform. HCP Terraform recognizes the alteration made and initiates a plan, which indicates that Terraform will create 3 new AWS EC2 instances; and that Terraform will submit an action request once the provisioned instances are created.&lt;/p&gt;

&lt;p&gt;After an engineer reviews and approves the proposed plan, Terraform executes the apply phase, creating three EC2 instances within AWS. At this time, the &lt;code&gt;action_trigger&lt;/code&gt; will be invoked. Terraform calls AAP's API, sending the newly created instance IDs and public IP addresses, to activate the patching job.&lt;/p&gt;

&lt;p&gt;The AAP will make a dynamic inventory through Terraform and subsequently will wait until all three instances can be accessed through SSH. Once that occurs, AAP will execute an &lt;code&gt;apt dist-upgrade&lt;/code&gt; command to identify whether a reboot is required, and then reboot the instance, if allowed. Finally, AAP will, upon each instance coming back online &amp;amp; responding normally, send a report back to Terraform.&lt;/p&gt;

&lt;p&gt;Upon the completion of the reports from AAP, Terraform will acknowledge completion of the run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Places Actions Are Immediately Useful
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CloudFront invalidation after S3 deployments&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudfront_create_invalidation"&lt;/span&gt; &lt;span class="s2"&gt;"bust_cache"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;distribution_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudfront_distribution&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;website&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;paths&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_object"&lt;/span&gt; &lt;span class="s2"&gt;"site_bundle"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_cloudfront_create_invalidation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bust_cache&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lambda warm-up after deployments&lt;/strong&gt;: After deployments, cold starts on the first production request are common sources of failures in Lambda functions. First, there may be cold starts on the first production request after deployments, which is typical of failures; hence, the function is invoked immediately after deployment to ensure users do not encounter any faults.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_invoke"&lt;/span&gt; &lt;span class="s2"&gt;"warm_up"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;function_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lambda_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;function_name&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"warmup"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"api_handler"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;events&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after_update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warm_up&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stopping dev instances on demand&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="s2"&gt;"aws_ec2_stop_instance"&lt;/span&gt; &lt;span class="s2"&gt;"stop_dev"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;instance_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-invoke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;action.aws_ec2_stop_instance.stop_dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chaining multiple actions&lt;/strong&gt; — &lt;code&gt;actions&lt;/code&gt; is a list, order is respected, each one completes before the next starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;action_trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;after_create&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ansible_aap_job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patch_servers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register_in_cmdb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_lambda_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notify_slack&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Things That Will Catch You Out
&lt;/h2&gt;

&lt;p&gt;An action that fails prevents a run's end:&lt;/p&gt;

&lt;p&gt;By default, how long Terraform waits for an action to finish is determined by the status of the action being waited on. This allows visibility into the status of actions, but introduces potential for issues if AAP has gone down just prior to a critical deployment, preventing Terraform from being able to wait for AAP to complete. Use &lt;code&gt;condition&lt;/code&gt; guards for actions that have minimal impact on the overall deployment if they're interrupted.&lt;/p&gt;

&lt;p&gt;Idempotency is not a luxury:&lt;/p&gt;

&lt;p&gt;Every time there is a resource change, the &lt;code&gt;after_update&lt;/code&gt; event fires. That means that your playbooks and lambda handlers will be invoked multiple times over the lifecycle of your infrastructure. It is acceptable to run &lt;code&gt;apt dist-upgrade&lt;/code&gt; multiple times but not acceptable to perform a database migration multiple times. You must design your programming for re-execution from the very beginning of the process.&lt;/p&gt;

&lt;p&gt;Actions do not write to state:&lt;/p&gt;

&lt;p&gt;When an action is executed there is no record of its execution being written to a statefile in terraform. The only way to tell that the action was executed is from the run history in HCP Terraform and in the logs for any other systems that were involved i.e., AAP job history, cloud watch, etc... You have to plan how you will be able to see/understand when your Terraform works based on its observability functionality.&lt;/p&gt;

&lt;p&gt;The provider support continues to grow:&lt;/p&gt;

&lt;p&gt;v1.14 of the AWS Provider supports a narrow set of operations (action types). Always refer to the Terraform Registry and Provider changelogs prior to assuming any operation is an action.&lt;/p&gt;

&lt;p&gt;CLI invocation requires existing resources:&lt;/p&gt;

&lt;p&gt;If your action references &lt;code&gt;instance.id&lt;/code&gt;, but the instance doesn't exist in state, the &lt;code&gt;-invoke&lt;/code&gt; option will fail during the &lt;code&gt;plan&lt;/code&gt; phase. Actions that use CLI should reference existing resources that have already been provisioned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Actual Shift
&lt;/h2&gt;

&lt;p&gt;Almost all infrastructure management involves Day-2 operations.&lt;/p&gt;

&lt;p&gt;In the past, Day-2 operations were recorded in runbooks, as part of a Jenkins job that only a few people understood, or as a bash script that was modified years ago. Day-2 operations were delivered in a reactive manner, which means if something breaks, somebody performs an action.&lt;/p&gt;

&lt;p&gt;With Terraform Actions, Day-2 operations can now be housed with the infrastructure they manage, from the same repository and pull request workflow and using the same audit trails as the infrastructure they are provisioned on. Patch management will be defined in terms of the infrastructure your organization will provision.&lt;/p&gt;

&lt;p&gt;This kind of change reduces the number of incidents occurring at 2:00 AM.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Terraform Actions is stable from Terraform CLI v1.14.0. Check &lt;a href="https://developer.hashicorp.com/terraform/language/invoke-actions" rel="noopener noreferrer"&gt;developer.hashicorp.com/terraform/language/invoke-actions&lt;/a&gt; for official documentation and your provider's registry page for supported action types.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Technical insights sourced from a community session on Terraform Day-2 operations.&lt;/em&gt;&lt;/p&gt;

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

</description>
      <category>vickybytes</category>
      <category>terraform</category>
      <category>aws</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
