<?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: Ali Raza</title>
    <description>The latest articles on DEV Community by Ali Raza (@ali_raza_fa80fd8371162ce6).</description>
    <link>https://dev.to/ali_raza_fa80fd8371162ce6</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%2F4033098%2F57677f9b-9eb6-48df-9e79-e360bb6352bf.png</url>
      <title>DEV Community: Ali Raza</title>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ali_raza_fa80fd8371162ce6"/>
    <language>en</language>
    <item>
      <title>Why Your Web App Feels Slow Even When Your Server Is Fast</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Fri, 16 Oct 2026 19:32:12 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/why-your-web-app-feels-slow-even-when-your-server-is-fast-20j2</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/why-your-web-app-feels-slow-even-when-your-server-is-fast-20j2</guid>
      <description>&lt;p&gt;&lt;em&gt;Understanding frontend bottlenecks, JavaScript execution, API latency, rendering delays, and the performance problems that server metrics often miss.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A web application can run on a powerful server, return API responses in milliseconds, and still feel frustratingly slow to its users.&lt;/p&gt;

&lt;p&gt;A button takes too long to respond. A dashboard appears in pieces. A page remains blank even though the network requests have completed. Scrolling becomes uncomfortable when a complex component renders, and a simple interaction occasionally freezes the entire interface.&lt;/p&gt;

&lt;p&gt;When developers investigate these problems, the first instinct is often to check server response times, database queries, and infrastructure resources.&lt;/p&gt;

&lt;p&gt;Those checks are important, but they do not tell the whole story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server speed and perceived application speed are two different things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A web application is a complete pipeline involving network communication, browser processing, JavaScript execution, rendering, asset delivery, and user interaction. A bottleneck anywhere in that pipeline can make the application feel slow, even when the backend is performing well.&lt;/p&gt;

&lt;p&gt;In this article, we will examine why that happens, how to identify the actual bottleneck, and which optimization techniques can improve the experience without unnecessary infrastructure changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Understanding the Difference Between Server Performance and User Experience
&lt;/h2&gt;

&lt;p&gt;Consider a web application that returns its main API response in 100 milliseconds.&lt;/p&gt;

&lt;p&gt;That sounds fast. However, the user might still wait several seconds before seeing usable content.&lt;/p&gt;

&lt;p&gt;The application may need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resolve the domain and establish a connection.&lt;/li&gt;
&lt;li&gt;Download the HTML document and JavaScript bundles.&lt;/li&gt;
&lt;li&gt;Parse and execute JavaScript.&lt;/li&gt;
&lt;li&gt;Request data from multiple API endpoints.&lt;/li&gt;
&lt;li&gt;Transform the received data.&lt;/li&gt;
&lt;li&gt;Render components and calculate layout.&lt;/li&gt;
&lt;li&gt;Download images and other visual assets.&lt;/li&gt;
&lt;li&gt;Wait for the interface to become interactive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some operations happen in parallel, while others depend on previous operations completing.&lt;/p&gt;

&lt;p&gt;For example, a dashboard might wait for a user profile before requesting personalized content. The content then needs to be processed before its components can render.&lt;/p&gt;

&lt;p&gt;Even if every individual API response is fast, a poorly designed sequence of dependent operations can introduce significant delays.&lt;/p&gt;

&lt;p&gt;This is why developers should distinguish between backend performance and the complete user-facing experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  A simple example
&lt;/h3&gt;

&lt;p&gt;Imagine a page with the following approximate timings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial HTML response&lt;/td&gt;
&lt;td&gt;100 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript download&lt;/td&gt;
&lt;td&gt;500 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript processing and execution&lt;/td&gt;
&lt;td&gt;700 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API request&lt;/td&gt;
&lt;td&gt;150 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rendering and layout work&lt;/td&gt;
&lt;td&gt;250 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These numbers are illustrative, not benchmark results. The operations may overlap, so adding them together does not necessarily produce the actual page load time.&lt;/p&gt;

&lt;p&gt;However, the example demonstrates an important point: a fast API does not guarantee that a page will become usable quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The objective is to measure the user's experience, not just the server's response.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. JavaScript Can Become the Main Performance Bottleneck
&lt;/h2&gt;

&lt;p&gt;Modern web applications often rely on JavaScript for routing, state management, data transformation, form validation, and dynamic rendering.&lt;/p&gt;

&lt;p&gt;This flexibility comes with a cost.&lt;/p&gt;

&lt;p&gt;Downloading JavaScript is only one part of the process. The browser must also parse, compile, and execute the code. Large bundles, expensive computations, and excessive rendering work can delay user interactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem with long-running JavaScript tasks
&lt;/h3&gt;

&lt;p&gt;JavaScript running on the browser's main thread can prevent the browser from responding promptly to user input while a long task is executing.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processLargeDataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&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;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;results&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;normalizedName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function is reasonable for a small collection. However, if it processes hundreds of thousands of objects synchronously, it can block the main thread long enough to make the interface unresponsive.&lt;/p&gt;

&lt;p&gt;A better approach depends on the workload.&lt;/p&gt;

&lt;p&gt;Possible improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Processing data in smaller chunks.&lt;/li&gt;
&lt;li&gt;Moving expensive computations into a Web Worker.&lt;/li&gt;
&lt;li&gt;Avoiding unnecessary transformations.&lt;/li&gt;
&lt;li&gt;Using pagination or virtualization for large datasets.&lt;/li&gt;
&lt;li&gt;Performing filtering and aggregation on the server when appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For computationally intensive work, a Web Worker can execute JavaScript away from the main UI thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/data-worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;largeDataset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nf"&gt;renderResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The worker file must implement the corresponding message handler and processing logic. The main thread can then receive the results without performing the entire computation itself.&lt;/p&gt;

&lt;p&gt;Workers have overhead and cannot directly manipulate the DOM, so they are most useful for independent computational tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce unnecessary JavaScript
&lt;/h3&gt;

&lt;p&gt;Bundle size also matters.&lt;/p&gt;

&lt;p&gt;A large dependency tree can increase download, parsing, and execution costs, particularly on slower devices.&lt;/p&gt;

&lt;p&gt;Useful techniques include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tree-shaking unused exports.&lt;/li&gt;
&lt;li&gt;Lazy-loading routes and expensive components.&lt;/li&gt;
&lt;li&gt;Splitting code according to user journeys.&lt;/li&gt;
&lt;li&gt;Removing unnecessary dependencies.&lt;/li&gt;
&lt;li&gt;Avoiding loading administrative features on public pages.&lt;/li&gt;
&lt;li&gt;Analyzing production bundles before adding new libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not simply to minimize the total number of kilobytes. It is to deliver the JavaScript required for the current task without making users wait for code they may never need.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Your API Can Be Fast While Your Application Is Slow
&lt;/h2&gt;

&lt;p&gt;A backend endpoint returning a response in 100 milliseconds sounds efficient. But the frontend may depend on several endpoints before it can display the information users need.&lt;/p&gt;

&lt;p&gt;One common problem is sequential API requests.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadDashboard&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;user&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;/api/user&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;courses&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="s2"&gt;`/api/courses?userId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;progress&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="s2"&gt;`/api/progress?userId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;progress&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 course and progress requests do not start until the user request finishes.&lt;/p&gt;

&lt;p&gt;If the user request takes 100 milliseconds and the other two each take 150 milliseconds, the request sequence can require approximately 400 milliseconds, excluding additional processing and network overhead.&lt;/p&gt;

&lt;p&gt;If the second and third requests are independent after the user ID is known, they can run concurrently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadDashboard&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;userResponse&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;/api/user&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="nx"&gt;userResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unable to load user&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;user&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;userResponse&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;coursesResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;progressResponse&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/courses?userId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/progress?userId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="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;coursesResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;progressResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unable to load dashboard data&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;coursesResponse&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="nx"&gt;progressResponse&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="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes the unnecessary sequential dependency between the course and progress requests.&lt;/p&gt;

&lt;p&gt;However, concurrency is not always the answer. Too many simultaneous requests can overload services, duplicate work, or consume unnecessary resources.&lt;/p&gt;

&lt;p&gt;Developers should identify actual dependencies before changing request patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other API-related bottlenecks
&lt;/h3&gt;

&lt;p&gt;Even when response times are low, the application can suffer from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetching entire datasets when only a few records are needed.&lt;/li&gt;
&lt;li&gt;Returning unnecessarily large JSON payloads.&lt;/li&gt;
&lt;li&gt;Repeatedly requesting the same data.&lt;/li&gt;
&lt;li&gt;Refetching data after every small interaction.&lt;/li&gt;
&lt;li&gt;Waiting for nonessential analytics or recommendations.&lt;/li&gt;
&lt;li&gt;Requiring multiple round trips for information that could be combined.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Measure the complete request flow, not just the duration reported by the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Rendering Too Much Can Make the UI Feel Unresponsive
&lt;/h2&gt;

&lt;p&gt;A frontend framework makes it easier to build complex interfaces, but it does not eliminate the cost of rendering them.&lt;/p&gt;

&lt;p&gt;A component may rerender because its state changes, its parent rerenders, or its props receive new references. Some rerenders are necessary. Others perform work that does not contribute to the visible result.&lt;/p&gt;

&lt;p&gt;Consider a dashboard displaying hundreds of cards, each with several nested components.&lt;/p&gt;

&lt;p&gt;If the application repeatedly recalculates every card after a small state change, users may notice delays when filtering, sorting, or opening a menu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common rendering problems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rendering large lists without virtualization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Displaying thousands of DOM elements creates layout, memory, and rendering overhead.&lt;/p&gt;

&lt;p&gt;List virtualization renders only the visible items and a small surrounding buffer. Libraries such as &lt;code&gt;react-window&lt;/code&gt; can help implement this approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performing expensive calculations during rendering&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Large sorting, filtering, or aggregation operations can make repeated renders costly.&lt;/p&gt;

&lt;p&gt;Move calculations to appropriate locations, cache results when useful, or process the data outside the main thread if the workload warrants it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recreating expensive components unnecessarily&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Memoization can help when a component repeatedly receives unchanged inputs and its rendering cost is significant.&lt;/p&gt;

&lt;p&gt;In React, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;memo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&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;CourseCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CourseCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;course&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;course&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;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;code&gt;memo&lt;/code&gt; can skip some rerenders when props remain equal under its comparison rules. It does not guarantee that every render will be skipped, and it will not prevent rerenders caused by the component's own state or consumed context.&lt;/p&gt;

&lt;p&gt;Measure before adding memoization everywhere. Unnecessary memoization adds complexity and can make code harder to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Large Images and Fonts Can Delay the Visible Page
&lt;/h2&gt;

&lt;p&gt;An application may have efficient JavaScript and fast APIs but still appear slow because its visual assets arrive late.&lt;/p&gt;

&lt;p&gt;Large hero images, unoptimized screenshots, oversized illustrations, and font files can delay rendering or cause visible layout shifts.&lt;/p&gt;

&lt;p&gt;This is especially important for content-heavy applications, dashboards, education platforms, and marketing websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimize image delivery
&lt;/h3&gt;

&lt;p&gt;Useful techniques include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serving appropriately sized images for each viewport.&lt;/li&gt;
&lt;li&gt;Using modern formats such as WebP or AVIF when supported by the delivery pipeline.&lt;/li&gt;
&lt;li&gt;Compressing images without unacceptable quality loss.&lt;/li&gt;
&lt;li&gt;Providing width and height dimensions to reduce layout shifts.&lt;/li&gt;
&lt;li&gt;Using responsive image candidates through &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Lazy-loading images that are below the fold.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/images/study-guide-800.webp"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
    /images/study-guide-400.webp 400w,
    /images/study-guide-800.webp 800w
  "&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 100vw, 800px"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"800"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"450"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Example of an organized study guide"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser can select a suitable image candidate based on the available information.&lt;/p&gt;

&lt;p&gt;Do not lazy-load the primary above-the-fold image if it is likely to be the Largest Contentful Paint element. Delaying that image can make the page feel slower.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fonts also matter
&lt;/h3&gt;

&lt;p&gt;Custom fonts may block or delay text rendering, introduce additional network requests, or cause layout shifts when fallback fonts are replaced.&lt;/p&gt;

&lt;p&gt;Consider using fewer font weights, serving appropriately subsetted font files, and applying &lt;code&gt;font-display: swap&lt;/code&gt; where suitable.&lt;/p&gt;

&lt;p&gt;The goal is to make meaningful content visible quickly, rather than waiting for every visual detail to load.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Core Web Vitals Help Explain What Users Experience
&lt;/h2&gt;

&lt;p&gt;Google's Core Web Vitals provide useful measurements for evaluating loading performance, responsiveness, and visual stability.&lt;/p&gt;

&lt;p&gt;The three current Core Web Vitals are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;th&gt;Good threshold&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Largest Contentful Paint (LCP)&lt;/td&gt;
&lt;td&gt;How quickly the main visible content loads&lt;/td&gt;
&lt;td&gt;2.5 seconds or less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interaction to Next Paint (INP)&lt;/td&gt;
&lt;td&gt;How responsive the page is to user interactions&lt;/td&gt;
&lt;td&gt;200 milliseconds or less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cumulative Layout Shift (CLS)&lt;/td&gt;
&lt;td&gt;Unexpected visual movement during page use&lt;/td&gt;
&lt;td&gt;0.1 or less&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These thresholds are evaluated at the 75th percentile of page visits, segmented by mobile and desktop where applicable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Largest Contentful Paint
&lt;/h3&gt;

&lt;p&gt;A slow LCP may indicate that the main content is delayed by server response time, render-blocking resources, JavaScript dependencies, or a large image.&lt;/p&gt;

&lt;p&gt;Investigate the actual LCP element before selecting an optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interaction to Next Paint
&lt;/h3&gt;

&lt;p&gt;A poor INP can indicate that clicks, keyboard input, or other interactions trigger excessive JavaScript work, expensive rendering, or delayed presentation of the result.&lt;/p&gt;

&lt;p&gt;Use browser performance traces to determine which part of the interaction is consuming time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cumulative Layout Shift
&lt;/h3&gt;

&lt;p&gt;A poor CLS often results from images without reserved dimensions, dynamically inserted content, or fonts that change the layout after rendering.&lt;/p&gt;

&lt;p&gt;Reserve space for content that loads asynchronously and avoid inserting elements above existing content without accounting for their dimensions.&lt;/p&gt;

&lt;p&gt;Core Web Vitals are valuable signals, but no single metric explains every performance problem. A page can meet the thresholds and still have frustrating interactions that deserve investigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Caching Can Reduce Repeated Work
&lt;/h2&gt;

&lt;p&gt;If users repeatedly request the same data, the application may be performing work that could have been avoided.&lt;/p&gt;

&lt;p&gt;Caching can reduce network requests, server load, and perceived waiting time.&lt;/p&gt;

&lt;p&gt;Different layers offer different opportunities:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser caching:&lt;/strong&gt; Store reusable static resources such as images, stylesheets, and versioned JavaScript bundles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CDN caching:&lt;/strong&gt; Deliver cacheable resources from locations closer to users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application data caching:&lt;/strong&gt; Reuse API results when the data remains valid for the current user and context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-side caching:&lt;/strong&gt; Avoid repeatedly calculating expensive results when they can safely be reused.&lt;/p&gt;

&lt;p&gt;HTTP caching headers, such as &lt;code&gt;Cache-Control&lt;/code&gt; and &lt;code&gt;ETag&lt;/code&gt;, can help browsers and intermediary caches determine whether a resource can be reused or needs revalidation.&lt;/p&gt;

&lt;p&gt;However, caching must respect data freshness, user permissions, and privacy.&lt;/p&gt;

&lt;p&gt;Personalized dashboards and private educational records should not be placed in shared caches without careful control. A fast response is not worth exposing one user's information to another.&lt;br&gt;
&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Measure Performance Before Making Changes
&lt;/h2&gt;

&lt;p&gt;One of the most expensive mistakes in performance optimization is fixing a problem that does not exist.&lt;/p&gt;

&lt;p&gt;Developers may compress assets, rewrite components, add caching layers, or upgrade infrastructure without identifying what users are actually waiting for.&lt;/p&gt;

&lt;p&gt;A more reliable process begins with measurement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Reproduce the problem
&lt;/h3&gt;

&lt;p&gt;Test the affected page under realistic conditions.&lt;/p&gt;

&lt;p&gt;Check whether the slowdown occurs on initial load, navigation, search, filtering, scrolling, or a particular user interaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Inspect the browser Network panel
&lt;/h3&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slow requests.&lt;/li&gt;
&lt;li&gt;Long dependency chains.&lt;/li&gt;
&lt;li&gt;Large response payloads.&lt;/li&gt;
&lt;li&gt;Duplicate API calls.&lt;/li&gt;
&lt;li&gt;Requests that block important content.&lt;/li&gt;
&lt;li&gt;Images and fonts that arrive too late.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember that the browser's reported timings may reflect several stages, including connection setup, waiting for the response, and downloading the body.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Use the Performance panel
&lt;/h3&gt;

&lt;p&gt;Chrome DevTools can help identify long tasks, JavaScript execution, layout recalculations, painting, and interaction delays.&lt;/p&gt;

&lt;p&gt;Record the problem while reproducing it. A trace can reveal whether the main thread is busy when the interface becomes unresponsive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Test with Lighthouse
&lt;/h3&gt;

&lt;p&gt;Lighthouse can identify common performance issues and provide diagnostic information about page loading, resources, and rendering.&lt;/p&gt;

&lt;p&gt;Use it as a starting point, not as a substitute for real-user data. Lab conditions may differ from users' devices, networks, and browsing behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Compare before and after
&lt;/h3&gt;

&lt;p&gt;Record the relevant metric, make one meaningful change, and test again under comparable conditions.&lt;/p&gt;

&lt;p&gt;For production websites, field data from real users can help confirm whether the improvement persists beyond a controlled development environment.&lt;/p&gt;

&lt;p&gt;Useful tools include Chrome DevTools, Lighthouse, PageSpeed Insights, and the browser's Performance APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. A Practical Performance Checklist for Developers
&lt;/h2&gt;

&lt;p&gt;Before increasing server capacity, review the entire application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Measure real user performance and identify the slowest user journeys.&lt;/li&gt;
&lt;li&gt;[ ] Check LCP, INP, and CLS for the affected pages.&lt;/li&gt;
&lt;li&gt;[ ] Identify large JavaScript bundles and unused dependencies.&lt;/li&gt;
&lt;li&gt;[ ] Investigate long tasks on the browser's main thread.&lt;/li&gt;
&lt;li&gt;[ ] Remove unnecessary sequential API requests.&lt;/li&gt;
&lt;li&gt;[ ] Reduce oversized responses and duplicate data fetching.&lt;/li&gt;
&lt;li&gt;[ ] Virtualize large lists when rendering costs justify it.&lt;/li&gt;
&lt;li&gt;[ ] Optimize images, fonts, and other critical assets.&lt;/li&gt;
&lt;li&gt;[ ] Cache reusable resources with appropriate freshness rules.&lt;/li&gt;
&lt;li&gt;[ ] Reserve layout space for asynchronously loaded content.&lt;/li&gt;
&lt;li&gt;[ ] Retest after each significant optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not every application needs every technique. Prioritize changes based on evidence, user impact, implementation cost, and regression risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Why This Matters for Modern Web Applications
&lt;/h2&gt;

&lt;p&gt;The distinction between server performance and perceived speed becomes especially important in applications that display frequently changing or generated content.&lt;/p&gt;

&lt;p&gt;For example, an educational web application may need to load study materials, render flashcards, display quiz results, and update learning progress. Even when its backend responds quickly, large client-side bundles, unnecessary rendering, or inefficient data flows can make the experience feel sluggish.&lt;/p&gt;

&lt;p&gt;A platform such as &lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;GoodOff&lt;/a&gt;, which helps students work with study materials in different learning formats, provides a relevant example of the kind of product where responsive interfaces and efficient content delivery can matter to the overall learning experience.&lt;/p&gt;

&lt;p&gt;This is not a claim about GoodOff's current performance or architecture. It illustrates why developers of content-rich applications should evaluate the complete user journey, from requesting material to interacting with the finished interface.&lt;/p&gt;

&lt;p&gt;When users are trying to learn, complete a task, or find information, unnecessary waiting can interrupt their workflow.&lt;/p&gt;

&lt;p&gt;Performance is therefore not just an infrastructure concern. It is part of product usability.&lt;/p&gt;

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

&lt;p&gt;A fast server is valuable, but it is only one component of a responsive web application.&lt;/p&gt;

&lt;p&gt;Users experience the entire system: network requests, JavaScript execution, data processing, rendering, asset delivery, and interaction feedback. A bottleneck in any of these areas can undermine the work invested in backend optimization.&lt;/p&gt;

&lt;p&gt;The most effective approach is to measure the experience, identify the actual bottleneck, make a targeted improvement, and verify the result.&lt;/p&gt;

&lt;p&gt;Start with the browser's Network and Performance panels. Examine Core Web Vitals. Investigate JavaScript execution, rendering behavior, API dependencies, and asset delivery before assuming that your server needs more resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real goal is not simply to make the server respond faster. It is to make the application feel fast when people use it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When developers optimize for the complete user experience, performance improvements become more measurable, more meaningful, and more likely to solve the problem users actually notice.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Suggested discussion question:&lt;/strong&gt; What has caused the biggest performance bottleneck in your web applications: JavaScript execution, API latency, rendering, or asset delivery? Share your experience in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your API Works Locally but Fails in Production: A Systematic Debugging Guide</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Wed, 14 Oct 2026 18:39:15 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/your-api-works-locally-but-fails-in-production-a-systematic-debugging-guide-495c</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/your-api-works-locally-but-fails-in-production-a-systematic-debugging-guide-495c</guid>
      <description>&lt;p&gt;Your API works perfectly on your laptop. The endpoints return the expected responses, database queries succeed, and your frontend communicates with the backend without any issues.&lt;/p&gt;

&lt;p&gt;Then you deploy.&lt;/p&gt;

&lt;p&gt;Suddenly, requests return &lt;code&gt;500 Internal Server Error&lt;/code&gt;. Authentication stops working. Database connections fail. Some endpoints work in Postman but fail in the browser. Other requests remain pending until they time out.&lt;/p&gt;

&lt;p&gt;The frustrating part is that you did not intentionally change the application logic.&lt;/p&gt;

&lt;p&gt;This is one of the most challenging parts of software development: &lt;strong&gt;an application that works in a development environment does not necessarily behave the same way in production.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The solution is not to randomly change configuration values or rewrite working code. It is to identify what changed between the two environments and isolate the failure systematically.&lt;/p&gt;

&lt;p&gt;In this guide, we will examine the most common causes of production API failures, practical debugging techniques, and a repeatable workflow developers can use to diagnose problems without wasting hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start by Identifying Where the Request Fails
&lt;/h2&gt;

&lt;p&gt;Before changing code, determine which part of the request lifecycle is failing.&lt;/p&gt;

&lt;p&gt;A typical API request passes through several stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The client sends an HTTP request.&lt;/li&gt;
&lt;li&gt;DNS resolves the API domain.&lt;/li&gt;
&lt;li&gt;A network connection is established.&lt;/li&gt;
&lt;li&gt;A reverse proxy or load balancer forwards the request.&lt;/li&gt;
&lt;li&gt;The application receives and processes it.&lt;/li&gt;
&lt;li&gt;Authentication and validation run.&lt;/li&gt;
&lt;li&gt;The application accesses a database or external service.&lt;/li&gt;
&lt;li&gt;The server returns a response.&lt;/li&gt;
&lt;li&gt;The client receives and processes that response.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A failure at any stage can make the API appear broken.&lt;/p&gt;

&lt;p&gt;For example, a &lt;code&gt;502 Bad Gateway&lt;/code&gt; may indicate that a proxy cannot communicate successfully with the upstream application. A &lt;code&gt;401 Unauthorized&lt;/code&gt; usually points toward authentication, while a &lt;code&gt;500 Internal Server Error&lt;/code&gt; indicates an unexpected server-side failure.&lt;/p&gt;

&lt;p&gt;These status codes are clues, not definitive diagnoses.&lt;/p&gt;

&lt;p&gt;Start with the browser's Network tab, your API client, and the server logs. Record the failing endpoint, HTTP method, status code, response body, and approximate timestamp.&lt;/p&gt;

&lt;p&gt;Then compare a successful local request with the failing production request.&lt;/p&gt;

&lt;p&gt;The objective is simple: identify the earliest stage where their behavior differs.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Missing or Incorrect Environment Variables
&lt;/h2&gt;

&lt;p&gt;Environment configuration is one of the first things to investigate when an application works locally but fails after deployment.&lt;/p&gt;

&lt;p&gt;Your local machine may contain a &lt;code&gt;.env&lt;/code&gt; file with values such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT=3000
DATABASE_URL=postgresql://localhost:5432/myapp
JWT_SECRET=local-development-secret
API_BASE_URL=http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, your production server may use a completely different configuration system.&lt;/p&gt;

&lt;p&gt;The deployment platform might not have all the required variables. A variable name may be incorrect, or the application may be reading a development URL instead of the production URL.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;databaseUrl&lt;/span&gt; &lt;span class="o"&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;DATABASE_URL&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;databaseUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_URL is not configured&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failing early with a clear error is better than allowing the application to start and fail unpredictably when the first database request arrives.&lt;/p&gt;

&lt;p&gt;You can also validate configuration during startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requiredVariables&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;DATABASE_URL&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;JWT_SECRET&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;API_BASE_URL&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;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;requiredVariables&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="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;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Missing required environment variable: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not print secret values to the logs. Log whether a required variable exists, not its contents.&lt;/p&gt;

&lt;p&gt;Also remember that frontend environment variables and backend environment variables are not necessarily interchangeable. Some frontend build systems embed configuration into the generated bundle during the build process. Changing a server variable after deployment may not update an already-built frontend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging tip:&lt;/strong&gt; Compare configuration names and non-sensitive settings between environments. Check whether the application was rebuilt or restarted after configuration changes.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://nodejs.org/api/process.html#processenv" rel="noopener noreferrer"&gt;Node.js documentation on environment variables&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. CORS Errors That Appear Only in Production
&lt;/h2&gt;

&lt;p&gt;Cross-Origin Resource Sharing, or CORS, is another common source of confusion.&lt;/p&gt;

&lt;p&gt;Imagine your frontend runs locally at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:5173&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Your backend runs at:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;During development, your backend might explicitly allow the local frontend origin.&lt;/p&gt;

&lt;p&gt;After deployment, the frontend moves to:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://app.example.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the backend does not allow that production origin, browser requests can fail even though the API itself is running correctly.&lt;/p&gt;

&lt;p&gt;For an Express application, a basic configuration might look like this:&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;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cors&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&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;allowedOrigins&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;http://localhost:5173&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;https://app.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;allowedOrigins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;methods&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;GET&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;POST&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;PUT&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;PATCH&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;DELETE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;allowedHeaders&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;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is only an example. Your allowed origins, methods, and headers should match your application's actual requirements.&lt;/p&gt;

&lt;p&gt;If your application uses cookies or other credentialed requests, additional configuration is needed. Credentialed CORS requests cannot use a wildcard origin.&lt;/p&gt;

&lt;p&gt;Another important distinction: CORS is enforced by browsers. A request succeeding in Postman does not prove that a browser request will succeed.&lt;br&gt;
&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
Check the browser console and inspect the response headers. Also verify that the server handles preflight &lt;code&gt;OPTIONS&lt;/code&gt; requests correctly.&lt;/p&gt;

&lt;p&gt;Avoid fixing CORS by allowing every origin without understanding the security implications.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS" rel="noopener noreferrer"&gt;MDN: Cross-Origin Resource Sharing&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Database Connections That Work Locally but Fail After Deployment
&lt;/h2&gt;

&lt;p&gt;Local development often uses a database running on the same machine. Production databases typically have different network boundaries, authentication rules, connection limits, and security requirements.&lt;/p&gt;

&lt;p&gt;A connection string that works locally may point to the wrong host in production.&lt;/p&gt;

&lt;p&gt;Other common causes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The production database does not allow connections from the application.&lt;/li&gt;
&lt;li&gt;Credentials are incorrect or expired.&lt;/li&gt;
&lt;li&gt;The database requires TLS.&lt;/li&gt;
&lt;li&gt;The application exceeds its connection pool limit.&lt;/li&gt;
&lt;li&gt;Database migrations have not been applied.&lt;/li&gt;
&lt;li&gt;The database hostname cannot be resolved.&lt;/li&gt;
&lt;li&gt;The database is temporarily unavailable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start by checking the exact database error in the server logs.&lt;/p&gt;

&lt;p&gt;A connection timeout suggests a different problem from an authentication failure or a missing table.&lt;/p&gt;

&lt;p&gt;For example, if your application reports that a relation does not exist, repeatedly changing firewall rules will not solve the issue. You need to investigate schema migrations and database selection.&lt;/p&gt;

&lt;p&gt;A useful production health check can verify database connectivity:&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/health&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT 1&lt;/span&gt;&lt;span class="dl"&gt;"&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;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;status&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;Database health check failed&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;name&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&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;code&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;503&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unavailable&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;p&gt;Here, &lt;code&gt;db.query()&lt;/code&gt; represents your database client's query method. Adapt it to your database library.&lt;/p&gt;

&lt;p&gt;Keep health checks separate from detailed diagnostic endpoints. Public health endpoints should not expose connection strings, internal hostnames, stack traces, or other sensitive infrastructure details.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Reverse Proxies, Ports, and Deployment Configuration
&lt;/h2&gt;

&lt;p&gt;Your application may listen on the correct port locally but fail behind a production proxy.&lt;/p&gt;

&lt;p&gt;Consider this code:&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;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can work locally, but some hosting environments expect the application to use a port supplied through an environment variable.&lt;/p&gt;

&lt;p&gt;A more flexible configuration is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&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;PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.0.0.0&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whether you need to bind to &lt;code&gt;0.0.0.0&lt;/code&gt; depends on your hosting environment. Some platforms manage networking differently, so follow their deployment requirements.&lt;/p&gt;

&lt;p&gt;You should also investigate reverse proxy configuration.&lt;/p&gt;

&lt;p&gt;For example, the proxy might forward &lt;code&gt;/api/users&lt;/code&gt; to an incorrect upstream address. It may also remove a URL prefix, impose a request body size limit, or terminate a connection before the application finishes processing.&lt;/p&gt;

&lt;p&gt;When investigating these problems, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the application process running?&lt;/li&gt;
&lt;li&gt;Is it listening on the expected port?&lt;/li&gt;
&lt;li&gt;Can the proxy reach the application?&lt;/li&gt;
&lt;li&gt;Are the forwarded paths correct?&lt;/li&gt;
&lt;li&gt;Are proxy and application timeout settings compatible?&lt;/li&gt;
&lt;li&gt;Do the proxy logs show an upstream connection error?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;code&gt;502&lt;/code&gt; or &lt;code&gt;504&lt;/code&gt; should lead you to investigate the proxy and upstream service rather than immediately changing application business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Production Builds Behave Differently
&lt;/h2&gt;

&lt;p&gt;Development and production builds may execute different code paths.&lt;/p&gt;

&lt;p&gt;A development server can provide hot reloading, local proxies, detailed error messages, and permissive settings. A production build may use optimized assets, different environment variables, stricter security rules, or a different startup command.&lt;/p&gt;

&lt;p&gt;For frontend applications, a development proxy may forward requests from &lt;code&gt;/api&lt;/code&gt; to your local backend. That proxy may not exist after deployment.&lt;/p&gt;

&lt;p&gt;For backend applications, a missing dependency, incorrect module format, or incompatible runtime version can prevent the application from starting.&lt;/p&gt;

&lt;p&gt;Reproduce the production build as closely as possible before deploying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
npm run build
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands are examples. Use the scripts defined by your project.&lt;/p&gt;

&lt;p&gt;Also verify the runtime version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
npm &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare the results with your production runtime. If your application depends on a particular Node.js version, declare that requirement and configure the deployment environment accordingly.&lt;/p&gt;

&lt;p&gt;For containerized applications, test the actual production container image rather than relying exclusively on your local development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Authentication and HTTPS Problems
&lt;/h2&gt;

&lt;p&gt;An endpoint may work locally because your development environment uses different authentication settings.&lt;/p&gt;

&lt;p&gt;Production introduces additional variables, including HTTPS, cookie security, OAuth redirect URLs, token expiration, and secret rotation.&lt;/p&gt;

&lt;p&gt;Suppose your frontend sends a bearer token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/profile&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;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the request returns &lt;code&gt;401&lt;/code&gt;, investigate whether the token is present, valid, unexpired, and signed with the expected key.&lt;/p&gt;

&lt;p&gt;If the server returns &lt;code&gt;403&lt;/code&gt;, the identity may be valid but lack the necessary permission. However, applications can use these status codes differently, so inspect your authentication middleware.&lt;/p&gt;

&lt;p&gt;For cookie-based authentication, verify the cookie's domain, &lt;code&gt;Secure&lt;/code&gt;, &lt;code&gt;SameSite&lt;/code&gt;, and path settings. Cross-site cookie behavior can differ significantly between localhost and production.&lt;/p&gt;

&lt;p&gt;Never solve a TLS certificate problem by disabling certificate verification. Fix the certificate chain, hostname, trust configuration, or server certificate instead.&lt;/p&gt;

&lt;p&gt;Useful checks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirming that the correct production identity provider is configured.&lt;/li&gt;
&lt;li&gt;Verifying allowed OAuth redirect URLs.&lt;/li&gt;
&lt;li&gt;Checking token issuer and audience validation.&lt;/li&gt;
&lt;li&gt;Confirming that cookies are actually sent by the browser.&lt;/li&gt;
&lt;li&gt;Ensuring that secret rotation has been applied consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Missing Logs Turn Small Problems Into Long Investigations
&lt;/h2&gt;

&lt;p&gt;When production fails, a developer needs evidence.&lt;/p&gt;

&lt;p&gt;A generic message such as &lt;code&gt;Something went wrong&lt;/code&gt; does not tell you whether the failure came from a database query, external API, validation rule, or application bug.&lt;/p&gt;

&lt;p&gt;Structured logging makes requests easier to trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;requestId&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;path&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;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;errorName&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;errorCode&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;code&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real application, use a logging library and middleware to generate or propagate request IDs.&lt;/p&gt;

&lt;p&gt;A request ID allows you to connect the same request across application logs, proxy logs, and downstream service logs.&lt;/p&gt;

&lt;p&gt;Be careful about logging request bodies, authorization headers, passwords, session cookies, and personal information. Diagnostic data should help you investigate the failure without creating a security incident.&lt;/p&gt;

&lt;p&gt;Production logging should capture enough information to answer four questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What operation failed?&lt;/li&gt;
&lt;li&gt;When did it fail?&lt;/li&gt;
&lt;li&gt;Which dependency or component was involved?&lt;/li&gt;
&lt;li&gt;What error category explains the failure?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For larger systems, distributed tracing can help identify where latency accumulates across multiple services.&lt;/p&gt;

&lt;p&gt;References: &lt;a href="https://expressjs.com/en/guide/debugging/" rel="noopener noreferrer"&gt;Express debugging guide&lt;/a&gt; and &lt;a href="https://docs.nestjs.com/deployment" rel="noopener noreferrer"&gt;NestJS deployment and observability guidance&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. A Systematic Debugging Workflow
&lt;/h2&gt;

&lt;p&gt;Instead of trying fixes randomly, follow a consistent process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Reproduce the failure
&lt;/h3&gt;

&lt;p&gt;Record the endpoint, HTTP method, request payload, response status, and timestamp. Use a test account and non-sensitive data whenever possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Compare environments
&lt;/h3&gt;

&lt;p&gt;Compare runtime versions, environment variable names, API base URLs, database configuration, network access, and build commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Inspect the response
&lt;/h3&gt;

&lt;p&gt;Look at response headers, response bodies, browser console messages, and network timing. Determine whether the request reached the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Check server-side evidence
&lt;/h3&gt;

&lt;p&gt;Search logs using the request timestamp or request ID. Identify the first meaningful error rather than focusing only on the final HTTP status.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Test dependencies independently
&lt;/h3&gt;

&lt;p&gt;Verify database connectivity, external API availability, DNS resolution, authentication, and network access from the production environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Change one variable at a time
&lt;/h3&gt;

&lt;p&gt;Apply the smallest reasonable fix. Changing several settings simultaneously makes it harder to identify the actual cause.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Verify the fix
&lt;/h3&gt;

&lt;p&gt;Repeat the original failing request. Test nearby cases, inspect logs, and confirm that the change has not introduced a new problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Prevent recurrence
&lt;/h3&gt;

&lt;p&gt;Add regression tests, configuration validation, monitoring, or deployment checks that catch the same failure earlier next time.&lt;/p&gt;

&lt;p&gt;This process may feel slower than immediately changing code, but it usually reduces wasted debugging time.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Build a Production Readiness Checklist
&lt;/h2&gt;

&lt;p&gt;Before deploying an API, verify the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Required environment variables are validated at startup.&lt;/li&gt;
&lt;li&gt;[ ] Production secrets are configured securely.&lt;/li&gt;
&lt;li&gt;[ ] The application uses the expected runtime version.&lt;/li&gt;
&lt;li&gt;[ ] Database connectivity and migrations are verified.&lt;/li&gt;
&lt;li&gt;[ ] CORS allows only the required origins.&lt;/li&gt;
&lt;li&gt;[ ] Authentication and authorization work in the production environment.&lt;/li&gt;
&lt;li&gt;[ ] Reverse proxy routes and timeout settings are correct.&lt;/li&gt;
&lt;li&gt;[ ] Logs include request IDs and useful error context.&lt;/li&gt;
&lt;li&gt;[ ] Health checks distinguish application availability from dependency health.&lt;/li&gt;
&lt;li&gt;[ ] External services have sensible timeout and retry policies.&lt;/li&gt;
&lt;li&gt;[ ] Production builds are tested before release.&lt;/li&gt;
&lt;li&gt;[ ] A rollback procedure exists for failed deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not every application needs the same infrastructure. A small API may not require distributed tracing or a complex monitoring platform. The important thing is to introduce controls that match your application's risks and operational requirements.&lt;/p&gt;

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

&lt;p&gt;When an API works locally but fails in production, the problem is not necessarily the code itself. It may be configuration, networking, database access, authentication, deployment behavior, or an interaction between several components.&lt;/p&gt;

&lt;p&gt;The most effective approach is to isolate the failing stage, compare the two environments, collect evidence, and test one hypothesis at a time.&lt;/p&gt;

&lt;p&gt;Developers who treat production debugging as a repeatable engineering process spend less time guessing and more time fixing the actual cause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal is not just to make the API work again. It is to understand why it failed and make the next failure easier to diagnose.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Debug AI-Generated Code Without Blindly Copying Its Fixes</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Mon, 12 Oct 2026 18:42:42 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-debug-ai-generated-code-without-blindly-copying-its-fixes-5h0l</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-debug-ai-generated-code-without-blindly-copying-its-fixes-5h0l</guid>
      <description>&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;AI coding assistants can generate functions, explain stack traces, suggest fixes, and even modify several files in a single task. For developers, this can significantly reduce the time spent writing repetitive code.&lt;/p&gt;

&lt;p&gt;However, generating a fix and verifying a fix are two different engineering tasks.&lt;/p&gt;

&lt;p&gt;Imagine that your API crashes when a user submits an empty email address. You ask an AI assistant to fix it. The assistant adds a condition, the error disappears, and the application starts working again.&lt;/p&gt;

&lt;p&gt;But what if the change only hides the error? What if it breaks valid requests, introduces a security weakness, or leaves another code path unprotected?&lt;/p&gt;

&lt;p&gt;This is where developers need a reliable debugging process.&lt;/p&gt;

&lt;p&gt;Instead of accepting an AI-generated solution because it looks reasonable, you should treat it as a hypothesis that needs evidence. The objective is not to stop using AI. It is to use AI in a way that makes debugging more systematic, predictable, and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Understand the Bug Before Asking AI to Fix It
&lt;/h2&gt;

&lt;p&gt;One of the most common mistakes in AI-assisted development is describing a problem too vaguely.&lt;/p&gt;

&lt;p&gt;A developer might write:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My API is not working. Fix the code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI assistant has almost no useful information. It must guess the intended behavior, the possible failure, and the surrounding architecture. Even if it produces valid code, the solution may not address the real problem.&lt;/p&gt;

&lt;p&gt;A better approach is to collect evidence before requesting a solution.&lt;/p&gt;

&lt;p&gt;Start by identifying four things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expected behavior:&lt;/strong&gt; What should the application do?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual behavior:&lt;/strong&gt; What happens instead?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproduction steps:&lt;/strong&gt; How can you consistently trigger the problem?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relevant evidence:&lt;/strong&gt; What do the logs, error messages, tests, or network requests reveal?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, instead of saying that a login endpoint is broken, describe the specific behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected:
A valid login request should return a user session.

Actual:
The endpoint returns HTTP 500 when the email field is missing.

Reproduction:
Send a POST request to /api/login with an empty JSON object.

Recent change:
The request validation logic was modified in the latest commit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the assistant a concrete problem to investigate.&lt;/p&gt;

&lt;p&gt;It also gives you a way to verify the proposed solution later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The principle is simple: do not ask AI to guess what is wrong when you can provide evidence about what is wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reproduce the Bug Before Changing the Code
&lt;/h2&gt;

&lt;p&gt;A bug that cannot be reproduced is difficult to diagnose reliably.&lt;/p&gt;

&lt;p&gt;Before editing the implementation, try to reproduce the issue in a controlled environment. Use the same inputs, request parameters, application state, and relevant configuration whenever possible.&lt;/p&gt;

&lt;p&gt;For a web application, useful debugging evidence may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser console errors&lt;/li&gt;
&lt;li&gt;Network requests and responses&lt;/li&gt;
&lt;li&gt;Server logs&lt;/li&gt;
&lt;li&gt;Stack traces&lt;/li&gt;
&lt;li&gt;Database query results&lt;/li&gt;
&lt;li&gt;Failed automated tests&lt;/li&gt;
&lt;li&gt;Recent code changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose an endpoint returns an unexpected response. Before changing it, inspect the request and response in the browser's Network panel or through a tool such as &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
You might discover that the frontend sends a string where the backend expects a number. The apparent backend bug could actually be an input-contract problem between two components.&lt;/p&gt;

&lt;p&gt;Without reproduction, an AI assistant might change the backend to accommodate the wrong input. That may hide the original issue and create inconsistencies elsewhere.&lt;/p&gt;

&lt;p&gt;Once you have reproduced the problem, create a test that captures the failure if practical. This gives you a measurable way to determine whether the proposed fix works.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Ask AI to Investigate Before Asking It to Edit
&lt;/h2&gt;

&lt;p&gt;Many developers jump directly from an error message to a request for a code change. A more reliable workflow separates investigation from implementation.&lt;/p&gt;

&lt;p&gt;First, ask the assistant to analyze the evidence without modifying files.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analyze this bug without changing any files.

Here is the error message:
[Paste error]

Here is the relevant code:
[Paste code]

Expected behavior:
[Describe expected behavior]

Actual behavior:
[Describe actual behavior]

Please provide:
1. The most likely root causes.
2. Evidence supporting each hypothesis.
3. Other possible explanations.
4. The smallest test that could distinguish between them.
5. Any assumptions that need verification.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This encourages the assistant to identify competing explanations instead of immediately committing to one.&lt;/p&gt;

&lt;p&gt;You can then test the most plausible hypothesis and update your understanding based on the results.&lt;/p&gt;

&lt;p&gt;For example, a database timeout could result from a slow query, a connection pool limit, a network issue, or a lock held by another transaction. Rewriting the query before checking these possibilities might waste time or make the system harder to maintain.&lt;/p&gt;

&lt;p&gt;AI can help generate hypotheses, explain unfamiliar code, and suggest diagnostic commands. However, the evidence still needs to determine which hypothesis is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Follow the Smallest-Change Principle
&lt;/h2&gt;

&lt;p&gt;When AI modifies a large section of code to fix a small issue, reviewing the result becomes harder.&lt;/p&gt;

&lt;p&gt;A change that touches several unrelated functions introduces more opportunities for regressions. It can also make it difficult to determine which modification caused a new failure.&lt;/p&gt;

&lt;p&gt;Prefer the smallest change that satisfies the requirement.&lt;/p&gt;

&lt;p&gt;Consider a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;percentage&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;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;percentage&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;percentage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid discount inputs&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;return&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;percentage&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&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;Suppose the bug is that negative prices are accepted. The correct change might be a single validation condition.&lt;/p&gt;

&lt;p&gt;An AI assistant could instead introduce a new validation library, refactor the pricing module, change function signatures, and update unrelated formatting.&lt;/p&gt;

&lt;p&gt;Those changes might be justified in a larger refactoring task, but they are unnecessary if the immediate requirement is simply to reject negative prices.&lt;/p&gt;

&lt;p&gt;When reviewing a suggested fix, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does every changed line contribute to solving the problem?&lt;/li&gt;
&lt;li&gt;Has the assistant modified unrelated behavior?&lt;/li&gt;
&lt;li&gt;Does the change follow the project's existing conventions?&lt;/li&gt;
&lt;li&gt;Can the same result be achieved with a smaller patch?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Smaller changes are generally easier to test, review, explain, and revert.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Verify the Fix With Tests, Not Confidence
&lt;/h2&gt;

&lt;p&gt;A successful explanation from an AI assistant is not proof that the code works.&lt;/p&gt;

&lt;p&gt;The most important question is whether the implementation satisfies the required behavior under realistic conditions.&lt;/p&gt;

&lt;p&gt;Start with a regression test that fails before the fix and passes afterward.&lt;/p&gt;

&lt;p&gt;For the discount example, you might test valid inputs and invalid boundaries:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;calculateDiscount&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;calculates a valid discount&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accepts a zero discount&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&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="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejects negative prices&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid discount inputs&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rejects discounts above 100 percent&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;calculateDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid discount inputs&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;p&gt;This example uses Vitest, but the same principle applies to other testing frameworks.&lt;/p&gt;

&lt;p&gt;A useful test suite should cover more than the exact input that exposed the bug. It should also examine boundary conditions and related behavior.&lt;/p&gt;

&lt;p&gt;For a login endpoint, this could mean testing missing fields, malformed input, valid credentials, invalid credentials, and unauthorized access.&lt;/p&gt;

&lt;p&gt;For a database operation, it might include duplicate records, concurrent requests, transaction failures, and unexpected values.&lt;/p&gt;

&lt;p&gt;You can also ask AI to identify missing tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this patch and propose tests for:
- The original failure
- Boundary conditions
- Invalid input
- Regression risks
- Security-sensitive behavior

Do not assume the implementation is correct.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the resulting suggestions as a starting point, then inspect the tests yourself. A test can pass while checking the wrong behavior.&lt;/p&gt;

&lt;p&gt;GitHub's guidance on &lt;a href="https://docs.github.com/en/copilot/tutorials/review-ai-generated-code" rel="noopener noreferrer"&gt;reviewing AI-generated code&lt;/a&gt; similarly recommends functional checks, testing, architectural alignment, and human oversight.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Check Whether the Fix Introduces New Problems
&lt;/h2&gt;

&lt;p&gt;A patch can resolve the original bug while introducing another one.&lt;/p&gt;

&lt;p&gt;This is particularly important when the change affects authentication, authorization, database queries, file uploads, payment processing, or external API calls.&lt;/p&gt;

&lt;p&gt;Consider a simplified login check:&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;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;user&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;401&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="na"&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;Invalid credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An AI-generated patch might make the endpoint stop crashing by bypassing a failing user lookup or returning a default user object. The endpoint could now return a successful response, but the authentication logic might no longer be trustworthy.&lt;/p&gt;

&lt;p&gt;The immediate error has disappeared, yet the application is less secure.&lt;/p&gt;

&lt;p&gt;When reviewing a patch, examine:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input validation:&lt;/strong&gt; Are unexpected or malformed values handled safely?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication and authorization:&lt;/strong&gt; Does the change preserve the requirement that users can access only permitted resources?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error handling:&lt;/strong&gt; Does the application handle failures correctly, or does it simply suppress them?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data integrity:&lt;/strong&gt; Could the change create duplicate records, overwrite valid data, or leave a transaction incomplete?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependencies:&lt;/strong&gt; Has the assistant introduced a package that is unnecessary, unmaintained, or incorrectly named?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets and logging:&lt;/strong&gt; Does the change expose tokens, credentials, or sensitive user information?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Secure_Code_Review_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP Secure Code Review Cheat Sheet&lt;/a&gt; provides a useful framework for examining input validation, access control, data flow, business logic, and security boundaries.&lt;/p&gt;

&lt;p&gt;Automated scanners and tests can catch many problems, but they do not eliminate the need to understand what the code is supposed to protect.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Use Logs and Observability to Verify Real Behavior
&lt;/h2&gt;

&lt;p&gt;Passing tests is essential, but tests cannot reproduce every condition found in a production environment.&lt;/p&gt;

&lt;p&gt;Some bugs depend on network latency, concurrency, unexpected data, configuration differences, or external services.&lt;/p&gt;

&lt;p&gt;For these problems, inspect logs, metrics, traces, and the behavior of the application after deployment.&lt;/p&gt;

&lt;p&gt;Suppose an AI assistant changes a database query to improve performance. A local test may pass, but production requests may still time out because the real bottleneck is connection contention.&lt;/p&gt;

&lt;p&gt;Instead of asking the assistant to repeatedly rewrite the query, examine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query execution time&lt;/li&gt;
&lt;li&gt;Database connection usage&lt;/li&gt;
&lt;li&gt;Request latency&lt;/li&gt;
&lt;li&gt;Error frequency&lt;/li&gt;
&lt;li&gt;Resource consumption&lt;/li&gt;
&lt;li&gt;Changes in behavior after deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use this evidence to determine whether the proposed fix addresses the underlying cause.&lt;/p&gt;

&lt;p&gt;For higher-risk changes, deploy through a staged release or canary process when your infrastructure supports it. Monitor relevant metrics and keep a rollback plan available.&lt;/p&gt;

&lt;p&gt;The goal is to establish a chain of evidence: the original failure was reproduced, the cause was investigated, the patch passed tests, and the application behaved correctly under relevant operating conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Learn to Recognize Common AI Debugging Mistakes
&lt;/h2&gt;

&lt;p&gt;AI coding assistants can produce useful solutions, but developers should watch for recurring failure patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing the symptom instead of the cause
&lt;/h3&gt;

&lt;p&gt;An assistant may add a fallback value to prevent a crash without explaining why the expected value was missing.&lt;/p&gt;

&lt;p&gt;Ask where the invalid state originated and whether the fallback is correct according to the requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changing tests to make the code pass
&lt;/h3&gt;

&lt;p&gt;If a test fails, the implementation might be wrong. The test might also be outdated, but that requires evidence.&lt;/p&gt;

&lt;p&gt;Do not accept a patch that removes an assertion or weakens a test simply because the build becomes green.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inventing APIs or configuration options
&lt;/h3&gt;

&lt;p&gt;Generated code may refer to a method, package, parameter, or configuration setting that does not exist in your installed version.&lt;/p&gt;

&lt;p&gt;Check official documentation, inspect the dependency version, and run the relevant code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring project architecture
&lt;/h3&gt;

&lt;p&gt;A solution may work in isolation but violate established patterns for error handling, data access, authentication, or state management.&lt;/p&gt;

&lt;p&gt;Give the assistant relevant project documentation and examples, then verify that the implementation fits the actual codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overengineering a simple fix
&lt;/h3&gt;

&lt;p&gt;A complicated abstraction can create more maintenance work than the original bug.&lt;/p&gt;

&lt;p&gt;Ask whether the additional complexity is necessary and whether the simpler solution meets the requirements.&lt;/p&gt;

&lt;p&gt;These are reasons to inspect AI-generated code carefully, not reasons to reject AI assistance altogether.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. A Practical AI Debugging Workflow
&lt;/h2&gt;

&lt;p&gt;You can use the following process for everyday development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce:&lt;/strong&gt; Confirm the bug using specific steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collect evidence:&lt;/strong&gt; Inspect logs, stack traces, requests, and recent changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investigate:&lt;/strong&gt; Ask AI for plausible root causes without modifying the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form a hypothesis:&lt;/strong&gt; Choose an explanation supported by evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the hypothesis:&lt;/strong&gt; Use a minimal experiment or failing regression test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement:&lt;/strong&gt; Apply the smallest justified change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify:&lt;/strong&gt; Run the regression test, relevant test suite, and static analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review:&lt;/strong&gt; Inspect the diff for unrelated changes, security issues, and broken assumptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate:&lt;/strong&gt; Check application behavior in a representative environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document:&lt;/strong&gt; Record the root cause and why the fix works.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow does not require every bug to receive a lengthy investigation. A minor formatting issue may need only a quick check. A defect involving payments or access control deserves a much deeper review.&lt;/p&gt;

&lt;p&gt;The depth of verification should match the potential impact of failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. The Best Way to Prompt AI During Debugging
&lt;/h2&gt;

&lt;p&gt;The quality of a debugging interaction depends partly on the information and constraints you provide.&lt;/p&gt;

&lt;p&gt;Instead of repeatedly asking, "Can you fix this?", use a prompt that defines the task and limits unnecessary changes.&lt;/p&gt;

&lt;p&gt;Here is a reusable template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are helping me debug an existing application.

Problem:
[Describe the observed behavior]

Expected behavior:
[Describe the intended behavior]

Evidence:
[Paste logs, stack traces, relevant code, and test results]

Constraints:
- Do not change unrelated files.
- Do not remove or weaken existing tests.
- Do not introduce new dependencies without justification.
- Preserve existing architecture and public interfaces unless a change is necessary.
- Explain assumptions and uncertainty.

First, identify likely root causes.
Then propose the smallest test that can distinguish between them.
Wait for the test results before recommending a final patch.

After the fix, explain:
1. The root cause
2. Why the change resolves it
3. The tests required
4. Possible regressions
5. Any security or compatibility concerns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your coding tool can edit files directly, explicitly request an investigation-only phase before allowing modifications.&lt;/p&gt;

&lt;p&gt;This reduces the risk of a long sequence of speculative edits that leaves you with code you do not fully understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: AI Can Suggest the Fix, but Evidence Must Validate It
&lt;/h2&gt;

&lt;p&gt;AI coding assistants are changing how developers write and debug software. They can help explain unfamiliar code, generate test cases, identify suspicious logic, and explore possible solutions.&lt;/p&gt;

&lt;p&gt;But faster code generation does not remove the need for engineering judgment.&lt;/p&gt;

&lt;p&gt;A fix is not reliable because it sounds convincing, looks clean, or makes an error message disappear. It is reliable when it satisfies the requirements, passes meaningful tests, preserves essential system behavior, and withstands appropriate review.&lt;/p&gt;

&lt;p&gt;The most valuable developers will not necessarily be the ones who generate the most code with AI. They will be the ones who know how to evaluate its output, identify incorrect assumptions, and prove that a change works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use AI to accelerate the investigation. Use tests and evidence to decide whether the fix deserves to be trusted.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Understand Technical Documentation as a Beginner Developer</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Thu, 08 Oct 2026 19:22:26 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-understand-technical-documentation-as-a-beginner-developer-56i9</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-understand-technical-documentation-as-a-beginner-developer-56i9</guid>
      <description>&lt;p&gt;&lt;em&gt;Technical documentation can feel confusing when you are new to programming. Learn how to read documentation, find the information you need, understand examples, and turn documentation into practical knowledge.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest changes that happens when you move from beginner tutorials to real software development is the amount of documentation you need to read.&lt;/p&gt;

&lt;p&gt;A tutorial might tell you exactly what to type.&lt;/p&gt;

&lt;p&gt;Documentation usually does not.&lt;/p&gt;

&lt;p&gt;Instead, you may see unfamiliar terms, configuration options, code examples, parameters, return values, warnings, and links to other pages. When you are new to development, even a simple API documentation page can feel overwhelming.&lt;/p&gt;

&lt;p&gt;The problem is usually not that you are bad at programming.&lt;/p&gt;

&lt;p&gt;You simply have not learned &lt;strong&gt;how to read technical documentation yet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Documentation is a skill. Like programming itself, it becomes easier with practice.&lt;/p&gt;

&lt;p&gt;This guide explains how beginner developers can approach technical documentation without trying to understand every line at once.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Technical Documentation Feels Difficult
&lt;/h2&gt;

&lt;p&gt;Technical documentation is generally written to be useful for people who already understand the technology.&lt;/p&gt;

&lt;p&gt;That means documentation often assumes you already know certain concepts.&lt;/p&gt;

&lt;p&gt;For example, a documentation page might say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Pass the authentication token in the Authorization header."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are a beginner, several questions immediately appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is an authentication token?&lt;/li&gt;
&lt;li&gt;What is a header?&lt;/li&gt;
&lt;li&gt;Where do I get the token?&lt;/li&gt;
&lt;li&gt;How do I send it?&lt;/li&gt;
&lt;li&gt;What does Authorization mean?&lt;/li&gt;
&lt;li&gt;Where does this code belong?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A beginner may read the entire page hoping these questions will eventually become clear.&lt;/p&gt;

&lt;p&gt;Usually, that is not the most effective approach.&lt;/p&gt;

&lt;p&gt;Instead, identify &lt;strong&gt;what you are trying to accomplish first&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Start With a Specific Goal
&lt;/h1&gt;

&lt;p&gt;Do not begin by trying to read an entire documentation website.&lt;/p&gt;

&lt;p&gt;Start with a question.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;How do I install this library?&lt;/li&gt;
&lt;li&gt;How do I authenticate with this API?&lt;/li&gt;
&lt;li&gt;How do I create a user?&lt;/li&gt;
&lt;li&gt;How do I upload a file?&lt;/li&gt;
&lt;li&gt;How do I connect this SDK to my project?&lt;/li&gt;
&lt;li&gt;How do I handle an error?&lt;/li&gt;
&lt;li&gt;How do I retrieve data from this endpoint?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A specific goal gives you a reason to read the documentation.&lt;/p&gt;

&lt;p&gt;Imagine you are learning a payment API.&lt;/p&gt;

&lt;p&gt;Instead of reading every available page, your first goal might be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I want to create my first test payment."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now you can search the documentation for installation, authentication, creating a payment, and testing.&lt;/p&gt;

&lt;p&gt;This immediately reduces the amount of information you need to process.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Understand the Structure Before Reading the Details
&lt;/h1&gt;

&lt;p&gt;Most technical documentation follows some type of structure.&lt;/p&gt;

&lt;p&gt;You may find sections such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting Started&lt;/li&gt;
&lt;li&gt;Installation&lt;/li&gt;
&lt;li&gt;Quick Start&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Guides&lt;/li&gt;
&lt;li&gt;API Reference&lt;/li&gt;
&lt;li&gt;Examples&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;li&gt;Troubleshooting&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not immediately read everything.&lt;/p&gt;

&lt;p&gt;First, scan the navigation.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Where is the information I need?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are completely new to a technology, start with the &lt;strong&gt;Getting Started&lt;/strong&gt; or &lt;strong&gt;Quick Start&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;If you already understand the basics and need a specific function, the &lt;strong&gt;API Reference&lt;/strong&gt; may be more useful.&lt;/p&gt;

&lt;p&gt;Learning to navigate documentation is almost as important as understanding the content inside it.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Read the Quick Start First
&lt;/h1&gt;

&lt;p&gt;When available, the Quick Start section is usually one of the best places for beginners.&lt;/p&gt;

&lt;p&gt;It often gives you a minimal working example.&lt;/p&gt;

&lt;p&gt;For example, instead of explaining every feature of a JavaScript library, the documentation may show:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the package&lt;/li&gt;
&lt;li&gt;Import the library&lt;/li&gt;
&lt;li&gt;Create an object&lt;/li&gt;
&lt;li&gt;Call a method&lt;/li&gt;
&lt;li&gt;Display the result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do not worry if you do not understand everything immediately.&lt;/p&gt;

&lt;p&gt;Your first objective is to understand the &lt;strong&gt;overall flow&lt;/strong&gt;.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;What is being installed?&lt;/li&gt;
&lt;li&gt;What is being imported?&lt;/li&gt;
&lt;li&gt;What information is required?&lt;/li&gt;
&lt;li&gt;What function is being called?&lt;/li&gt;
&lt;li&gt;What result is produced?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you understand the basic flow, you can investigate individual parts.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;
&lt;/h2&gt;

&lt;h1&gt;
  
  
  4. Do Not Try to Understand Every Word
&lt;/h1&gt;

&lt;p&gt;This is one of the most important lessons for beginner developers.&lt;/p&gt;

&lt;p&gt;You do not need to understand every technical term before writing your first piece of code.&lt;/p&gt;

&lt;p&gt;Suppose documentation contains this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client.users.create({
  name: "John",
  email: "john@example.com"
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might not know exactly what &lt;code&gt;client&lt;/code&gt;, &lt;code&gt;users&lt;/code&gt;, or &lt;code&gt;create&lt;/code&gt; means yet.&lt;/p&gt;

&lt;p&gt;That is okay.&lt;/p&gt;

&lt;p&gt;Start by understanding the bigger picture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A client is being used to create a user, and information about that user is being provided.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then investigate the individual pieces.&lt;/p&gt;

&lt;p&gt;Trying to understand everything simultaneously can turn a five-minute task into an hour of confusion.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Learn the Difference Between Guides and API References
&lt;/h1&gt;

&lt;p&gt;Beginners often mix these two types of documentation.&lt;/p&gt;

&lt;p&gt;They serve different purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guides
&lt;/h2&gt;

&lt;p&gt;Guides explain &lt;strong&gt;how to accomplish something&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They may walk you through a complete process.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;How to authenticate your application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This type of documentation is usually easier for beginners.&lt;/p&gt;

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

&lt;p&gt;API references explain &lt;strong&gt;what a particular function, endpoint, class, or parameter does&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You might see information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function name&lt;/li&gt;
&lt;li&gt;Parameters&lt;/li&gt;
&lt;li&gt;Required values&lt;/li&gt;
&lt;li&gt;Optional values&lt;/li&gt;
&lt;li&gt;Return type&lt;/li&gt;
&lt;li&gt;Errors&lt;/li&gt;
&lt;li&gt;Examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;API references are extremely useful, but they can be difficult if you do not understand the underlying concepts.&lt;/p&gt;

&lt;p&gt;A good learning process is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guide first, reference second.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use the guide to understand the workflow, then use the reference when you need specific details.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Focus on Examples
&lt;/h1&gt;

&lt;p&gt;Code examples can sometimes teach you faster than paragraphs of explanation.&lt;/p&gt;

&lt;p&gt;When you find an example, do not simply copy and paste it.&lt;/p&gt;

&lt;p&gt;Read it line by line.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What does this line do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if I change this value?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, if the documentation shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ali&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Experiment with the example.&lt;/p&gt;

&lt;p&gt;Change the name.&lt;/p&gt;

&lt;p&gt;Remove a value.&lt;/p&gt;

&lt;p&gt;Try an invalid value.&lt;/p&gt;

&lt;p&gt;See what happens.&lt;/p&gt;

&lt;p&gt;This turns documentation from something you are reading into something you are interacting with.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Learn Important Documentation Terms
&lt;/h1&gt;

&lt;p&gt;A small vocabulary can make documentation much easier to understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameter
&lt;/h3&gt;

&lt;p&gt;A value that you provide to a function or method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Argument
&lt;/h3&gt;

&lt;p&gt;The actual value you pass when calling a function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Return Value
&lt;/h3&gt;

&lt;p&gt;The result produced by a function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Endpoint
&lt;/h3&gt;

&lt;p&gt;A specific URL where an API can receive or provide data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Request
&lt;/h3&gt;

&lt;p&gt;The information your application sends.&lt;/p&gt;

&lt;h3&gt;
  
  
  Response
&lt;/h3&gt;

&lt;p&gt;The information returned by a server or API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;A process used to verify who or what is accessing a system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Required
&lt;/h3&gt;

&lt;p&gt;A value that must be provided.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional
&lt;/h3&gt;

&lt;p&gt;A value that does not have to be provided.&lt;/p&gt;

&lt;p&gt;You do not need to memorize every technical term immediately.&lt;/p&gt;

&lt;p&gt;Keep a small glossary and add terms as you encounter them.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Turn Documentation Into Questions
&lt;/h1&gt;

&lt;p&gt;One of the most effective ways to learn technical documentation is to stop treating it like something you simply read.&lt;/p&gt;

&lt;p&gt;Turn it into questions.&lt;/p&gt;

&lt;p&gt;For example, after reading an API page, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is this API used for?&lt;/li&gt;
&lt;li&gt;How do I authenticate?&lt;/li&gt;
&lt;li&gt;What parameters are required?&lt;/li&gt;
&lt;li&gt;What does this endpoint return?&lt;/li&gt;
&lt;li&gt;What errors can occur?&lt;/li&gt;
&lt;li&gt;What is the simplest working example?&lt;/li&gt;
&lt;li&gt;What happens if a required parameter is missing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you are actively testing your understanding.&lt;/p&gt;

&lt;p&gt;This is especially useful when documentation is long.&lt;/p&gt;

&lt;p&gt;Instead of trying to remember everything, focus on answering the questions that matter for your project.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Turn Important Documentation Into Flashcards
&lt;/h1&gt;

&lt;p&gt;Some technical information needs to be remembered.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Programming concepts&lt;/li&gt;
&lt;li&gt;Syntax rules&lt;/li&gt;
&lt;li&gt;Terminology&lt;/li&gt;
&lt;li&gt;Common commands&lt;/li&gt;
&lt;li&gt;Framework concepts&lt;/li&gt;
&lt;li&gt;API parameters&lt;/li&gt;
&lt;li&gt;Important configuration options&lt;/li&gt;
&lt;li&gt;Keyboard shortcuts&lt;/li&gt;
&lt;li&gt;Development principles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can turn these into flashcards.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; What is the purpose of an API endpoint?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; It is a specific location through which a client can interact with a server or service.&lt;/p&gt;

&lt;p&gt;Flashcards should not replace hands-on coding.&lt;/p&gt;

&lt;p&gt;They should support it.&lt;/p&gt;

&lt;p&gt;A developer needs both &lt;strong&gt;knowledge and application&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. Use AI to Make Documentation Easier to Understand
&lt;/h1&gt;

&lt;p&gt;AI can be particularly useful when documentation contains concepts that are difficult to understand.&lt;/p&gt;

&lt;p&gt;Instead of asking an AI tool to write your entire application, use it as a learning assistant.&lt;/p&gt;

&lt;p&gt;For example, you can ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Explain this documentation in beginner-friendly language."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Explain each parameter in this example."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What do I need to understand before using this API?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can also ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Quiz me on this documentation without showing the answers first."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This creates an active learning process.&lt;/p&gt;

&lt;p&gt;GoodOff can also be useful when you are learning from technical PDFs, notes, documentation, or other study material. Students and beginner developers can turn source material into formats such as study guides, flashcards, and quizzes, making it easier to move from reading technical information to actively practicing it.&lt;/p&gt;

&lt;p&gt;The important thing is to use AI to &lt;strong&gt;increase understanding&lt;/strong&gt;, not to skip understanding.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Build a Study System Around Technical Documentation
&lt;/h1&gt;

&lt;p&gt;If you are learning a new technology, you can turn its documentation into a structured study routine.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Step 1: Choose one topic
&lt;/h3&gt;

&lt;p&gt;Do not try to learn an entire framework in one day.&lt;/p&gt;

&lt;p&gt;Choose something specific, such as authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Read the introductory guide
&lt;/h3&gt;

&lt;p&gt;Understand what the feature does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Follow the example
&lt;/h3&gt;

&lt;p&gt;Run the code yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Experiment
&lt;/h3&gt;

&lt;p&gt;Change values and see what happens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Create questions
&lt;/h3&gt;

&lt;p&gt;Write down questions about the concepts you need to remember.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Create flashcards
&lt;/h3&gt;

&lt;p&gt;Turn important concepts into recall questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Take a quiz
&lt;/h3&gt;

&lt;p&gt;Test whether you actually understand the topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Build something
&lt;/h3&gt;

&lt;p&gt;Use the concept in a small project.&lt;/p&gt;

&lt;p&gt;This final step is critical.&lt;/p&gt;

&lt;p&gt;Understanding documentation becomes much easier when you have a real reason to use it.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. Do Not Depend Completely on AI-Generated Code
&lt;/h1&gt;

&lt;p&gt;AI can explain documentation and generate examples, but blindly copying AI-generated code can create another problem.&lt;/p&gt;

&lt;p&gt;You may end up with working code that you do not understand.&lt;/p&gt;

&lt;p&gt;That becomes especially dangerous when something breaks.&lt;/p&gt;

&lt;p&gt;Instead, use AI to explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why the code works&lt;/li&gt;
&lt;li&gt;What each function does&lt;/li&gt;
&lt;li&gt;What the parameters mean&lt;/li&gt;
&lt;li&gt;What assumptions the code makes&lt;/li&gt;
&lt;li&gt;What could cause an error&lt;/li&gt;
&lt;li&gt;How you could test it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then try writing or modifying the code yourself.&lt;/p&gt;

&lt;p&gt;The goal is not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI writes code → Developer copies code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A better process is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation → Understanding → Experimentation → AI assistance → Testing → Understanding again&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  13. Keep a Personal Documentation Knowledge Base
&lt;/h1&gt;

&lt;p&gt;As you become a developer, you will repeatedly encounter useful information.&lt;/p&gt;

&lt;p&gt;Do not rely entirely on memory.&lt;/p&gt;

&lt;p&gt;Create your own collection of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Important commands&lt;/li&gt;
&lt;li&gt;Common errors&lt;/li&gt;
&lt;li&gt;Useful code patterns&lt;/li&gt;
&lt;li&gt;Documentation links&lt;/li&gt;
&lt;li&gt;Concepts you struggled with&lt;/li&gt;
&lt;li&gt;Solutions you discovered&lt;/li&gt;
&lt;li&gt;Framework notes&lt;/li&gt;
&lt;li&gt;API examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, this becomes a personal technical knowledge base.&lt;/p&gt;

&lt;p&gt;The next time you encounter the same problem, you will not have to start from zero.&lt;/p&gt;

&lt;p&gt;This can also help you identify gaps in your knowledge.&lt;/p&gt;

&lt;p&gt;If you repeatedly save notes about authentication, for example, that may be a sign that authentication deserves deeper study.&lt;/p&gt;




&lt;h1&gt;
  
  
  14. Practice With Real Projects
&lt;/h1&gt;

&lt;p&gt;Reading documentation without applying it can quickly become passive learning.&lt;/p&gt;

&lt;p&gt;After learning a concept, build something small.&lt;/p&gt;

&lt;p&gt;If you are learning an API, build a simple application that uses it.&lt;/p&gt;

&lt;p&gt;If you are learning a database library, create a small CRUD project.&lt;/p&gt;

&lt;p&gt;If you are learning authentication, build a basic login system.&lt;/p&gt;

&lt;p&gt;The project does not need to be impressive.&lt;/p&gt;

&lt;p&gt;It needs to force you to use the documentation.&lt;/p&gt;

&lt;p&gt;Every time you get stuck, return to the relevant section.&lt;/p&gt;

&lt;p&gt;This creates a powerful cycle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read → Build → Get stuck → Search documentation → Understand → Continue building&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is how documentation gradually becomes familiar.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simple 45-Minute Documentation Study Routine
&lt;/h1&gt;

&lt;p&gt;If you are learning a new technology, try this routine:&lt;/p&gt;

&lt;h3&gt;
  
  
  10 minutes: Read
&lt;/h3&gt;

&lt;p&gt;Choose one small documentation section.&lt;/p&gt;

&lt;h3&gt;
  
  
  10 minutes: Understand
&lt;/h3&gt;

&lt;p&gt;Identify unfamiliar terms and concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  10 minutes: Practice
&lt;/h3&gt;

&lt;p&gt;Follow the example in your own development environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  10 minutes: Recall
&lt;/h3&gt;

&lt;p&gt;Close the documentation and explain the concept from memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  5 minutes: Review
&lt;/h3&gt;

&lt;p&gt;Write down what you still do not understand.&lt;/p&gt;

&lt;p&gt;You can then use those weak areas to guide your next study session.&lt;/p&gt;

&lt;p&gt;This is far more manageable than trying to read hundreds of pages at once.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Technical documentation is not something developers eventually stop needing.&lt;/p&gt;

&lt;p&gt;It becomes one of the most important resources in everyday development.&lt;/p&gt;

&lt;p&gt;The difference is that experienced developers usually become better at &lt;strong&gt;finding the right information quickly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a beginner, do not measure your progress by how much documentation you can read.&lt;/p&gt;

&lt;p&gt;Measure it by what you can understand and use.&lt;/p&gt;

&lt;p&gt;Start with a specific goal.&lt;/p&gt;

&lt;p&gt;Find the relevant guide.&lt;/p&gt;

&lt;p&gt;Study the example.&lt;/p&gt;

&lt;p&gt;Understand the important terms.&lt;/p&gt;

&lt;p&gt;Experiment with the code.&lt;/p&gt;

&lt;p&gt;Turn difficult concepts into questions.&lt;/p&gt;

&lt;p&gt;Use flashcards and quizzes to reinforce important information.&lt;/p&gt;

&lt;p&gt;Then build something with what you learned.&lt;/p&gt;

&lt;p&gt;Tools such as GoodOff can support this process by helping students and beginner developers transform technical learning material into structured study resources instead of relying only on passive reading.&lt;/p&gt;

&lt;p&gt;You do not need to memorize an entire documentation website.&lt;/p&gt;

&lt;p&gt;You need to learn how to &lt;strong&gt;navigate it, understand it, question it, and use it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once that skill develops, documentation stops looking like a wall of technical information and starts becoming one of your most useful tools as a developer.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Learn Programming With AI Without Skipping the Fundamentals</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Wed, 07 Oct 2026 17:58:44 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-learn-programming-with-ai-without-skipping-the-fundamentals-554</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-learn-programming-with-ai-without-skipping-the-fundamentals-554</guid>
      <description>&lt;p&gt;Artificial intelligence has changed the way people learn programming.&lt;/p&gt;

&lt;p&gt;A beginner can now ask an AI assistant to explain an error, generate a function, write a SQL query, build a webpage, or even create an entire application. What once required hours of searching through documentation can sometimes be reduced to a few well-written prompts.&lt;/p&gt;

&lt;p&gt;That convenience is powerful, but it creates an important question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are developers actually learning programming, or are they simply learning how to ask AI for code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI can be an excellent programming tutor, but only when it is used to strengthen understanding rather than replace it. GitHub itself warns that AI-generated code can appear correct while being inaccurate or unsuitable, and recommends reviewing and testing generated code rather than treating it as automatically trustworthy. &lt;br&gt;
The best approach is therefore not to avoid AI.&lt;/p&gt;

&lt;p&gt;It is to &lt;strong&gt;learn programming with AI while keeping the fundamentals at the center of the process&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Why Programming Fundamentals Still Matter in the AI Era&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Programming languages and development tools will continue to change.&lt;/p&gt;

&lt;p&gt;Python may become more popular. New JavaScript frameworks will appear. AI coding assistants will become more capable. Development environments will continue to automate repetitive tasks.&lt;/p&gt;

&lt;p&gt;But fundamental programming concepts remain useful across technologies.&lt;/p&gt;

&lt;p&gt;A developer who understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;Conditions&lt;/li&gt;
&lt;li&gt;Loops&lt;/li&gt;
&lt;li&gt;Data structures&lt;/li&gt;
&lt;li&gt;Algorithms&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Version control&lt;/li&gt;
&lt;li&gt;Software architecture&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;can transfer those skills to different languages and frameworks.&lt;/p&gt;

&lt;p&gt;Someone who only knows how to generate code may struggle when the generated solution does not work.&lt;/p&gt;

&lt;p&gt;This is the difference between &lt;strong&gt;using AI to write code&lt;/strong&gt; and &lt;strong&gt;understanding the code AI writes&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;The Biggest Risk of Learning Programming With AI&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;The biggest problem is not that AI generates code.&lt;/p&gt;

&lt;p&gt;The problem is when beginners accept that code without understanding it.&lt;/p&gt;

&lt;p&gt;Imagine a student asks an AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build me a login system in Python."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI produces several files, database logic, authentication functions, validation, and error handling.&lt;/p&gt;

&lt;p&gt;The application might even work.&lt;/p&gt;

&lt;p&gt;But if the student cannot explain how authentication works, why passwords need secure handling, what a database query is doing, or how the application responds to invalid input, they have not necessarily learned programming.&lt;/p&gt;

&lt;p&gt;They have learned how to obtain a solution.&lt;/p&gt;

&lt;p&gt;That distinction becomes important when the problem changes.&lt;/p&gt;

&lt;p&gt;AI can generate an answer for a familiar request. A developer needs to understand how to reason when the request is unfamiliar.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Use AI as a Tutor, Not a Shortcut&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;One of the best ways to learn programming with AI is to change the role you give it.&lt;/p&gt;

&lt;p&gt;Instead of saying:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Write the code for me."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;try asking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Explain the concept first, then give me a small example."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Fix this code."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;try:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Help me identify why this code is failing. Give me hints before showing the solution."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Build this project."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;try:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Break this project into smaller programming concepts and help me implement them one at a time."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This forces you to participate in the learning process.&lt;/p&gt;

&lt;p&gt;GitHub's own documentation includes a learning workflow where Copilot can be configured to act as a tutor, explain basic coding concepts, and focus on helping the learner understand the overall approach rather than simply providing solutions. ([GitHub Docs][2])&lt;/p&gt;

&lt;p&gt;That is a much healthier relationship with AI for beginners.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Learn the Concept Before the Framework&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;A common mistake among new developers is jumping directly into frameworks.&lt;/p&gt;

&lt;p&gt;A beginner might start with React, Next.js, Django, Laravel, or another framework without understanding the underlying concepts.&lt;/p&gt;

&lt;p&gt;Frameworks are useful, but they do not replace fundamentals.&lt;/p&gt;

&lt;p&gt;Before asking AI to help you build a React application, for example, understand basic JavaScript concepts.&lt;/p&gt;

&lt;p&gt;Before building a database-driven application, understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What data is&lt;/li&gt;
&lt;li&gt;What tables are&lt;/li&gt;
&lt;li&gt;What relationships are&lt;/li&gt;
&lt;li&gt;What queries do&lt;/li&gt;
&lt;li&gt;What CRUD means&lt;/li&gt;
&lt;li&gt;Why validation matters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before building an API, understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests&lt;/li&gt;
&lt;li&gt;Responses&lt;/li&gt;
&lt;li&gt;HTTP methods&lt;/li&gt;
&lt;li&gt;Status codes&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Data formats such as JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI can help explain all of these concepts.&lt;/p&gt;

&lt;p&gt;But you should learn the underlying idea before relying on the framework to hide it.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Use the Explain, Predict, Test Method&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;A simple learning system can help prevent AI from doing all the thinking.&lt;/p&gt;

&lt;p&gt;Use three stages:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Explain&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ask AI to explain the programming concept in simple language.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"Explain recursion as if I understand functions but have never used recursion."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Focus on the idea.&lt;/p&gt;

&lt;p&gt;Do not immediately copy code.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. Predict&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before asking AI for the solution, try to predict what should happen.&lt;/p&gt;

&lt;p&gt;If you are debugging a program, ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What should this function return?&lt;/li&gt;
&lt;li&gt;Which line should execute first?&lt;/li&gt;
&lt;li&gt;What value should this variable contain?&lt;/li&gt;
&lt;li&gt;What happens when the input changes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This develops your programming reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. Test&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Write your own attempt.&lt;/p&gt;

&lt;p&gt;Run it.&lt;/p&gt;

&lt;p&gt;Look at the result.&lt;/p&gt;

&lt;p&gt;Only then use AI to help identify what went wrong.&lt;/p&gt;

&lt;p&gt;This process makes AI part of your learning loop instead of replacing the loop.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Ask AI for Hints Before Asking for Answers&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;When you are stuck, do not immediately ask for the complete solution.&lt;/p&gt;

&lt;p&gt;Try a progression.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Level 1: Ask for a hint&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Give me one hint about what I should check."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Level 2: Ask for an explanation&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Explain the concept I need to understand to solve this."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Level 3: Ask for debugging guidance&lt;/strong&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Point out where my reasoning is going wrong without rewriting the code."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Level 4: Ask for a solution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Only after trying yourself should you ask AI to demonstrate a complete approach.&lt;/p&gt;

&lt;p&gt;This method preserves the productive struggle that comes with learning programming.&lt;/p&gt;

&lt;p&gt;The goal is not to make every problem easy.&lt;/p&gt;

&lt;p&gt;The goal is to become better at solving problems.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Do Not Copy Code You Cannot Explain&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;A simple rule can dramatically improve AI-assisted learning:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you cannot explain the code, do not consider the task finished.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose AI generates a function containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A loop&lt;/li&gt;
&lt;li&gt;A conditional statement&lt;/li&gt;
&lt;li&gt;An array method&lt;/li&gt;
&lt;li&gt;An asynchronous operation&lt;/li&gt;
&lt;li&gt;An API request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should be able to explain what each major part does.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What problem does this code solve?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is this approach being used?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if the input changes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if the API fails?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the limitations of this solution?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you cannot answer these questions, stop and learn the underlying concepts.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Build Small Projects Instead of Generating Large Applications&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;AI makes it incredibly easy to generate large projects.&lt;/p&gt;

&lt;p&gt;That can actually hurt beginners.&lt;/p&gt;

&lt;p&gt;If you ask AI to build an entire task-management application, you might receive thousands of lines of code.&lt;/p&gt;

&lt;p&gt;It can look impressive.&lt;/p&gt;

&lt;p&gt;But it can also become impossible to understand.&lt;/p&gt;

&lt;p&gt;Instead, build small projects.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  &lt;strong&gt;Project 1&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Build a simple calculator.&lt;/p&gt;

&lt;p&gt;Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;Conditions&lt;/li&gt;
&lt;li&gt;Input handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Project 2&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Build a to-do list.&lt;/p&gt;

&lt;p&gt;Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arrays&lt;/li&gt;
&lt;li&gt;Objects&lt;/li&gt;
&lt;li&gt;DOM manipulation&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Project 3&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Build a small API.&lt;/p&gt;

&lt;p&gt;Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP&lt;/li&gt;
&lt;li&gt;Routes&lt;/li&gt;
&lt;li&gt;Requests&lt;/li&gt;
&lt;li&gt;Responses&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Project 4&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Connect an application to a database.&lt;/p&gt;

&lt;p&gt;Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data modeling&lt;/li&gt;
&lt;li&gt;Queries&lt;/li&gt;
&lt;li&gt;CRUD operations&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI can help at every stage, but the project should remain small enough for you to understand.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Use AI to Explain Documentation, Not Replace It&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Programming requires reading documentation.&lt;/p&gt;

&lt;p&gt;There is no realistic way around it.&lt;/p&gt;

&lt;p&gt;Developers regularly need to understand APIs, libraries, frameworks, configuration files, error messages, and technical specifications.&lt;/p&gt;

&lt;p&gt;AI can make documentation easier to understand.&lt;/p&gt;

&lt;p&gt;Instead of asking AI to replace the documentation, give it the relevant section and ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Explain this documentation in simple terms and give me a small example."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then go back to the original documentation.&lt;/p&gt;

&lt;p&gt;This creates an important habit:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI helps you understand the source. The source remains the authority.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is especially important because AI can provide outdated or incorrect technical information.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Turn Documentation Into a Personal Learning System&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Long technical documentation can be difficult to study.&lt;/p&gt;

&lt;p&gt;Instead of repeatedly reading the same pages, developers can convert important concepts into questions.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;br&gt;
What does an HTTP 404 status code indicate?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt;&lt;br&gt;
The requested resource could not be found.&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt;&lt;br&gt;
What is the purpose of a database index?&lt;/p&gt;

&lt;p&gt;The goal is to turn passive reading into retrieval practice.&lt;/p&gt;

&lt;p&gt;Tools such as GoodOff can support this workflow by transforming source material into different learning formats. A developer can bring PDFs, notes, documents, slides, or other material into GoodOff and create flashcards, quizzes, study guides, and source-grounded explanations. ([GoodOff][3])&lt;/p&gt;

&lt;p&gt;Its flashcard system is designed around active recall and uses FSRS spaced repetition to schedule reviews. ([GoodOff][4])&lt;/p&gt;

&lt;p&gt;This can be particularly useful when learning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Programming documentation&lt;/li&gt;
&lt;li&gt;Course notes&lt;/li&gt;
&lt;li&gt;Certification material&lt;/li&gt;
&lt;li&gt;Technical PDFs&lt;/li&gt;
&lt;li&gt;API documentation&lt;/li&gt;
&lt;li&gt;Computer science concepts&lt;/li&gt;
&lt;li&gt;Software architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to create more notes.&lt;/p&gt;

&lt;p&gt;The goal is to create more opportunities to &lt;strong&gt;retrieve and apply what you learned&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Use Quizzes to Discover What You Actually Know&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;One of the easiest ways to overestimate your programming knowledge is to recognize concepts while reading them.&lt;/p&gt;

&lt;p&gt;You may think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I understand this."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But can you answer a question about it without looking?&lt;/p&gt;

&lt;p&gt;Try testing yourself.&lt;/p&gt;

&lt;p&gt;After studying functions, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is a function?&lt;/li&gt;
&lt;li&gt;Why are functions useful?&lt;/li&gt;
&lt;li&gt;What is a parameter?&lt;/li&gt;
&lt;li&gt;What is a return value?&lt;/li&gt;
&lt;li&gt;What happens if a function receives unexpected input?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After studying APIs, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is an API?&lt;/li&gt;
&lt;li&gt;What is an endpoint?&lt;/li&gt;
&lt;li&gt;What is an HTTP request?&lt;/li&gt;
&lt;li&gt;What is the difference between GET and POST?&lt;/li&gt;
&lt;li&gt;What does a 404 response mean?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GoodOff can generate quizzes from your learning material and provide explanations for answers, allowing the student to identify gaps instead of relying only on rereading. ([GoodOff][5])&lt;/p&gt;

&lt;p&gt;This is particularly useful for technical subjects because understanding a concept and being able to retrieve it are different skills.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Use AI to Debug Your Thinking&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Debugging is one of the most important programming skills.&lt;/p&gt;

&lt;p&gt;AI can be extremely useful here, but there is a better approach than simply pasting an error and copying the response.&lt;/p&gt;

&lt;p&gt;First, describe what you expected.&lt;/p&gt;

&lt;p&gt;Then explain what actually happened.&lt;/p&gt;

&lt;p&gt;Then show the relevant code.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"I expected the function to return three items, but it returns an empty array. Here is what I expected, what I observed, and the relevant code. Help me identify possible causes."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This teaches you to communicate technical problems clearly.&lt;/p&gt;

&lt;p&gt;You can then ask AI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What assumptions am I making?&lt;/li&gt;
&lt;li&gt;What should I test first?&lt;/li&gt;
&lt;li&gt;Which variable should I inspect?&lt;/li&gt;
&lt;li&gt;What possible causes could produce this result?&lt;/li&gt;
&lt;li&gt;What experiment would eliminate each possibility?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now AI becomes a debugging partner rather than a code vending machine.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Always Test AI-Generated Code&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;This should be one of the strongest rules in AI-assisted programming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generated code is not automatically correct because it compiles.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub's documentation specifically notes that AI-generated code can be inaccurate and recommends careful review and testing. It also warns that generated code may introduce security concerns or fail to fit the architecture and style of an existing codebase. &lt;/p&gt;

&lt;p&gt;Therefore, after AI generates code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read it.&lt;/li&gt;
&lt;li&gt;Understand the logic.&lt;/li&gt;
&lt;li&gt;Run it.&lt;/li&gt;
&lt;li&gt;Test normal inputs.&lt;/li&gt;
&lt;li&gt;Test unusual inputs.&lt;/li&gt;
&lt;li&gt;Test failure cases.&lt;/li&gt;
&lt;li&gt;Check security implications.&lt;/li&gt;
&lt;li&gt;Compare it with official documentation.&lt;/li&gt;
&lt;li&gt;Review it as if another developer wrote it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process is not just about safety.&lt;/p&gt;

&lt;p&gt;It is also learning.&lt;/p&gt;

&lt;p&gt;Every review teaches you something about programming.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Use AI to Increase Practice, Not Reduce It&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;The best use of AI may not be generating more code.&lt;/p&gt;

&lt;p&gt;It may be generating more &lt;strong&gt;practice opportunities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, ask AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Teach me arrays through five progressively difficult exercises."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Give me a debugging problem involving a loop. Do not show the solution until I attempt it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Give me three API design questions and evaluate my answers."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a small programming challenge that tests whether I understand recursion."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now AI is helping you practice.&lt;/p&gt;

&lt;p&gt;That is fundamentally different from asking it to complete every assignment.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Create a Developer Learning Loop&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;A strong AI-assisted learning routine can look like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn → Attempt → Get Stuck → Ask AI → Understand → Implement → Test → Explain → Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each step has a purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Learn&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Understand the concept.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Attempt&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Try solving the problem yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Get Stuck&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Identify exactly where you are struggling.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Ask AI&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use AI for explanation or guidance.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Understand&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Make sure you understand the solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Implement&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Write or modify the code yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Test&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Verify that it actually works.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Explain&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Explain the solution in your own words.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Review&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Return to the concept later and test yourself again.&lt;/p&gt;

&lt;p&gt;This creates a learning process where AI accelerates the journey without removing the important parts.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Where GoodOff Can Fit Into Developer Learning&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;GoodOff is primarily a learning platform, but its source-based workflow can also be useful for developers who are studying technical material.&lt;/p&gt;

&lt;p&gt;For example, imagine you are learning a new programming language and have a 100-page PDF course guide.&lt;/p&gt;

&lt;p&gt;Instead of rereading the entire document every weekend, you can turn that source into:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Study Guide&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Understand the major concepts, definitions, and questions. GoodOff's study guide workflow organizes source material into an overview, key concepts, definitions, likely questions, and memory aids. ([GoodOff][6])&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flashcards&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Turn important programming concepts into active-recall questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quizzes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Test whether you actually understand the material.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sage Tutor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask questions about the material while keeping the learning grounded in your uploaded source. GoodOff says Sage provides answers with citations to the passages it used. ([GoodOff][3])&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spaced Repetition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Return to important concepts over time instead of relying on one long study session.&lt;/p&gt;

&lt;p&gt;This creates a useful distinction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI writes code. AI also helps you learn.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those are two different uses of AI, and the second one can help developers become better at the first.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;A Practical AI Programming Study Routine&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;If you are learning programming today, try this simple routine.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;First: Learn one concept&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Spend 30 to 60 minutes learning a specific concept.&lt;/p&gt;

&lt;p&gt;Do not try to learn an entire framework in one sitting.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Second: Write something yourself&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Create a small exercise using that concept.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Third: Ask AI for help only when necessary&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Start with hints and explanations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Fourth: Test your solution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Try normal and unexpected inputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Fifth: Explain what you built&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you cannot explain it, identify what you do not understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Sixth: Turn difficult concepts into questions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Create flashcards or quiz questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Seventh: Review later&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use spaced repetition to revisit concepts that are easy to forget.&lt;/p&gt;

&lt;p&gt;This process may feel slower than simply generating an application.&lt;/p&gt;

&lt;p&gt;But the objective is not to generate an application.&lt;/p&gt;

&lt;p&gt;The objective is to &lt;strong&gt;become a developer who can build applications&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;The Future Developer Will Need More Than Coding Speed&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;AI is making code generation faster.&lt;/p&gt;

&lt;p&gt;That means typing speed and memorizing syntax may become less important than they once were.&lt;/p&gt;

&lt;p&gt;But other skills become more important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding requirements&lt;/li&gt;
&lt;li&gt;Breaking problems into smaller parts&lt;/li&gt;
&lt;li&gt;Evaluating AI-generated solutions&lt;/li&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Security awareness&lt;/li&gt;
&lt;li&gt;Reading documentation&lt;/li&gt;
&lt;li&gt;Understanding architecture&lt;/li&gt;
&lt;li&gt;Making technical decisions&lt;/li&gt;
&lt;li&gt;Knowing when AI is wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 2025 systematic literature review of AI in computer programming education found that research has increasingly shifted toward practical classroom implementation and assessment of AI-supported programming education&lt;/p&gt;

&lt;p&gt;The technology is changing quickly.&lt;/p&gt;

&lt;p&gt;The need for strong reasoning is not disappearing.&lt;/p&gt;

&lt;p&gt;If anything, it is becoming more important.&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;strong&gt;Conclusion: Learn With AI, Not Instead of Learning&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;AI has made programming more accessible than ever.&lt;/p&gt;

&lt;p&gt;A beginner can ask questions at any time, receive explanations, generate examples, analyze errors, and explore unfamiliar concepts.&lt;/p&gt;

&lt;p&gt;But the best programmers will not be the people who ask AI to do everything.&lt;/p&gt;

&lt;p&gt;They will be the people who understand &lt;strong&gt;what AI is doing, why it works, when it is wrong, and how to improve it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Use AI to explain difficult concepts.&lt;/p&gt;

&lt;p&gt;Use it to create practice problems.&lt;/p&gt;

&lt;p&gt;Use it to help debug your reasoning.&lt;/p&gt;

&lt;p&gt;Use it to simplify documentation.&lt;/p&gt;

&lt;p&gt;Use it to generate quizzes and flashcards from technical learning material.&lt;/p&gt;

&lt;p&gt;But keep the fundamentals.&lt;/p&gt;

&lt;p&gt;Learn how programming works.&lt;/p&gt;

&lt;p&gt;Write code yourself.&lt;/p&gt;

&lt;p&gt;Break problems down.&lt;/p&gt;

&lt;p&gt;Make mistakes.&lt;/p&gt;

&lt;p&gt;Debug them.&lt;/p&gt;

&lt;p&gt;Test your solutions.&lt;/p&gt;

&lt;p&gt;And explain what you built.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why Your Website Is Slow Even When Your Server Is Fast</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Tue, 06 Oct 2026 18:02:41 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/why-your-website-is-slow-even-when-your-server-is-fast-35m3</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/why-your-website-is-slow-even-when-your-server-is-fast-35m3</guid>
      <description>&lt;p&gt;A common web performance misconception is that a fast server automatically means a fast website.&lt;/p&gt;

&lt;p&gt;It does not.&lt;/p&gt;

&lt;p&gt;Your server may return the initial HTML quickly, yet users can still experience a page that feels slow, unresponsive, or unstable. The reason is simple: &lt;strong&gt;server response time is only one part of the browser's work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After receiving the first byte, the browser still has to download resources, parse HTML, process CSS, execute JavaScript, decode images, build the page, render content, and respond to user interactions.&lt;/p&gt;

&lt;p&gt;This is why developers should stop treating performance as a purely backend problem.&lt;/p&gt;

&lt;p&gt;Modern web performance is a full pipeline involving the &lt;strong&gt;network, server, browser, JavaScript, CSS, images, third-party scripts, and rendering process&lt;/strong&gt;. Google's Core Web Vitals reflect this broader experience through metrics such as Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). ([Google for Developers][1])&lt;/p&gt;




&lt;h2&gt;
  
  
  The Server Can Be Fast While the Website Is Slow
&lt;/h2&gt;

&lt;p&gt;Consider a simple example.&lt;/p&gt;

&lt;p&gt;Your server responds to an HTML request in 300 milliseconds.&lt;/p&gt;

&lt;p&gt;That sounds excellent.&lt;/p&gt;

&lt;p&gt;But then the browser downloads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 MB of JavaScript&lt;/li&gt;
&lt;li&gt;1.5 MB of images&lt;/li&gt;
&lt;li&gt;Several CSS files&lt;/li&gt;
&lt;li&gt;Multiple third-party scripts&lt;/li&gt;
&lt;li&gt;Web fonts&lt;/li&gt;
&lt;li&gt;Analytics libraries&lt;/li&gt;
&lt;li&gt;Tracking pixels&lt;/li&gt;
&lt;li&gt;API requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser now has substantial work to perform before the user can interact comfortably with the page.&lt;/p&gt;

&lt;p&gt;The result could look 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;Server response:       300 ms
HTML parsing:          200 ms
CSS processing:        150 ms
JavaScript download:   900 ms
JavaScript execution:  1,200 ms
Image decoding:        500 ms
Third-party scripts:   700 ms
Rendering:              400 ms
--------------------------------
Actual user experience: much slower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact numbers will vary, but the principle is important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fast TTFB does not guarantee fast LCP, INP, or overall user experience.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TTFB, or Time to First Byte, measures the time between navigation and the beginning of the response. It includes factors such as DNS, connection setup, TLS negotiation, and server processing. web.dev recommends roughly 0.8 seconds or less as a general TTFB target, but also emphasizes that TTFB is not itself a Core Web Vital. ([web.dev][2])&lt;/p&gt;




&lt;h1&gt;
  
  
  1. JavaScript May Be Your Real Bottleneck
&lt;/h1&gt;

&lt;p&gt;One of the biggest reasons modern websites become slow is JavaScript.&lt;/p&gt;

&lt;p&gt;Developers often focus on the size of JavaScript files, but file size is only part of the problem.&lt;/p&gt;

&lt;p&gt;The browser has to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download JavaScript&lt;/li&gt;
&lt;li&gt;Parse it&lt;/li&gt;
&lt;li&gt;Compile it&lt;/li&gt;
&lt;li&gt;Execute it&lt;/li&gt;
&lt;li&gt;Perform DOM operations&lt;/li&gt;
&lt;li&gt;Run event handlers&lt;/li&gt;
&lt;li&gt;Process framework code&lt;/li&gt;
&lt;li&gt;Recalculate styles&lt;/li&gt;
&lt;li&gt;Render updates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A JavaScript file can therefore affect performance even after it has finished downloading.&lt;/p&gt;

&lt;p&gt;For example:&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;calculateSomething&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A task like this can occupy the browser's main thread.&lt;/p&gt;

&lt;p&gt;While the main thread is busy, user interactions may have to wait.&lt;br&gt;
&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
This matters particularly for &lt;strong&gt;Interaction to Next Paint&lt;/strong&gt;, or INP. INP measures how responsive a page is to user interactions throughout the page lifecycle. Google currently recommends an INP below 200 milliseconds for a good experience. ([Google for Developers][1])&lt;/p&gt;
&lt;h3&gt;
  
  
  What developers can do
&lt;/h3&gt;

&lt;p&gt;Start by identifying unnecessary JavaScript.&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large framework bundles&lt;/li&gt;
&lt;li&gt;Unused libraries&lt;/li&gt;
&lt;li&gt;Duplicate dependencies&lt;/li&gt;
&lt;li&gt;Heavy UI components&lt;/li&gt;
&lt;li&gt;Unnecessary animations&lt;/li&gt;
&lt;li&gt;Large client-side calculations&lt;/li&gt;
&lt;li&gt;Scripts loaded on pages where they are not needed&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/app.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where appropriate, rather than forcing scripts to block HTML parsing.&lt;/p&gt;

&lt;p&gt;For larger applications, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code splitting&lt;/li&gt;
&lt;li&gt;Tree shaking&lt;/li&gt;
&lt;li&gt;Dynamic imports&lt;/li&gt;
&lt;li&gt;Lazy loading&lt;/li&gt;
&lt;li&gt;Removing unused dependencies&lt;/li&gt;
&lt;li&gt;Moving expensive work away from the main thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not simply to make JavaScript smaller.&lt;/p&gt;

&lt;p&gt;The goal is to &lt;strong&gt;make the browser do less work&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Your Images May Be Slowing Down the Page
&lt;/h1&gt;

&lt;p&gt;Images are another common performance problem.&lt;/p&gt;

&lt;p&gt;A developer may upload a 4 MB image because the original looks high quality on a desktop monitor.&lt;/p&gt;

&lt;p&gt;The browser does not care that the original file looks good.&lt;/p&gt;

&lt;p&gt;It still has to download it.&lt;/p&gt;

&lt;p&gt;Imagine a landing page with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hero image       3.2 MB
Feature image    1.8 MB
Team image       1.1 MB
Background       1.5 MB
Other images     2.0 MB
----------------------
Total            9.6 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can become a serious problem, particularly on mobile networks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better approach
&lt;/h3&gt;

&lt;p&gt;Use modern image formats such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebP&lt;/li&gt;
&lt;li&gt;AVIF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Serve images at appropriate dimensions and avoid downloading a 2000px-wide image when the browser only displays it at 500px.&lt;/p&gt;

&lt;p&gt;Responsive images can help:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"image-800.webp"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"
    image-400.webp 400w,
    image-800.webp 800w,
    image-1200.webp 1200w"&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 100vw, 800px"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Product dashboard"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For images that are below the fold, lazy loading can also reduce initial network work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"feature.webp"&lt;/span&gt;
  &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Feature illustration"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, do not blindly lazy-load your most important above-the-fold image.&lt;/p&gt;

&lt;p&gt;Your primary LCP element should be discoverable and prioritized appropriately. Google's LCP guidance specifically recommends making the LCP resource discoverable from the HTML and avoiding unnecessary JavaScript dependency before it can render. ([web.dev][3])&lt;/p&gt;




&lt;h1&gt;
  
  
  3. CSS Can Also Delay Rendering
&lt;/h1&gt;

&lt;p&gt;CSS looks lightweight compared with JavaScript, but large or poorly structured stylesheets can still affect rendering.&lt;/p&gt;

&lt;p&gt;Consider a website loading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global.css
theme.css
components.css
animations.css
responsive.css
plugin.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If most of these styles are required before the page can render, the browser has additional work before presenting the final page.&lt;/p&gt;

&lt;p&gt;Developers should examine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unused CSS&lt;/li&gt;
&lt;li&gt;Large CSS frameworks&lt;/li&gt;
&lt;li&gt;Excessive selectors&lt;/li&gt;
&lt;li&gt;Duplicate styles&lt;/li&gt;
&lt;li&gt;Unnecessary animations&lt;/li&gt;
&lt;li&gt;Render-blocking stylesheets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Critical styles for the initial viewport can sometimes be prioritized while non-critical styles are loaded later.&lt;/p&gt;

&lt;p&gt;The objective is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deliver the styles needed for the first screen as quickly as possible.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Third-Party Scripts Can Quietly Destroy Performance
&lt;/h1&gt;

&lt;p&gt;This is one of the most overlooked problems.&lt;/p&gt;

&lt;p&gt;Your application may be fast.&lt;/p&gt;

&lt;p&gt;Your server may be fast.&lt;/p&gt;

&lt;p&gt;Your code may be optimized.&lt;/p&gt;

&lt;p&gt;Then someone adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analytics
Chat widget
Heatmaps
Advertising
Social media pixels
A/B testing
Customer support
Consent management
Embedded videos
Marketing automation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suddenly the browser has dozens of additional requests and scripts to process.&lt;/p&gt;

&lt;p&gt;Third-party resources can also introduce connection latency because the browser may need to establish connections to external origins. web.dev specifically notes that resources hosted on cross-origin servers can introduce additional latency during connection setup. ([web.dev][2])&lt;/p&gt;

&lt;h3&gt;
  
  
  What should developers do?
&lt;/h3&gt;

&lt;p&gt;Audit third-party scripts regularly.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Does this script directly contribute to the user's current task?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If not, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading it later&lt;/li&gt;
&lt;li&gt;Loading it after interaction&lt;/li&gt;
&lt;li&gt;Loading it asynchronously&lt;/li&gt;
&lt;li&gt;Removing it entirely&lt;/li&gt;
&lt;li&gt;Replacing multiple tools with one solution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every third-party script should have a performance cost that the team consciously accepts.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Too Many API Requests Can Make a Fast Server Feel Slow
&lt;/h1&gt;

&lt;p&gt;Suppose your API responds in 100 milliseconds.&lt;/p&gt;

&lt;p&gt;That sounds fast.&lt;/p&gt;

&lt;p&gt;But imagine the frontend makes 15 sequential API requests.&lt;/p&gt;

&lt;p&gt;If requests depend on one another, the user may experience a much longer wait.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A
   ↓
Request B
   ↓
Request C
   ↓
Request D
   ↓
Render
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even fast individual requests can create slow user experiences when chained together.&lt;/p&gt;

&lt;p&gt;Where possible, independent requests can run in parallel:&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;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;fetchRecommendations&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;Developers should also look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate API requests&lt;/li&gt;
&lt;li&gt;Sequential requests that could run in parallel&lt;/li&gt;
&lt;li&gt;Over-fetching&lt;/li&gt;
&lt;li&gt;Large JSON responses&lt;/li&gt;
&lt;li&gt;Unnecessary polling&lt;/li&gt;
&lt;li&gt;Missing caching&lt;/li&gt;
&lt;li&gt;API calls triggered repeatedly by UI updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question should not only be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How fast is my API?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should also be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How much work does the browser need from my API before the user can use the page?"&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  6. Caching Can Be More Important Than Server Speed
&lt;/h1&gt;

&lt;p&gt;A request that never reaches the server is faster than a request handled by the fastest server.&lt;/p&gt;

&lt;p&gt;Caching can reduce unnecessary network and server work.&lt;/p&gt;

&lt;p&gt;Depending on your application, caching can exist at several levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser Cache
      ↓
CDN Cache
      ↓
Application Cache
      ↓
Database Cache
      ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Static resources such as CSS, JavaScript, fonts, and versioned images are often strong candidates for long-lived caching.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: public, max-age=31536000, immutable
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This strategy works particularly well for assets with content-hashed filenames:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.82f31a.js
styles.91af21.css
logo.18d9c2.webp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the file changes, the filename changes.&lt;/p&gt;

&lt;p&gt;That allows browsers to cache the old asset without creating stale-content problems.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Your Database Can Be Fast Yet Your Application Can Still Be Slow
&lt;/h1&gt;

&lt;p&gt;Another common mistake is measuring database query time independently.&lt;/p&gt;

&lt;p&gt;A query might take 30 milliseconds.&lt;/p&gt;

&lt;p&gt;But the application may perform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query user
Query permissions
Query subscription
Query products
Query recommendations
Query notifications
Query analytics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the total backend processing time is significantly higher.&lt;/p&gt;

&lt;p&gt;This is where application profiling becomes important.&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;N+1 queries&lt;/li&gt;
&lt;li&gt;Unnecessary joins&lt;/li&gt;
&lt;li&gt;Missing indexes&lt;/li&gt;
&lt;li&gt;Repeated queries&lt;/li&gt;
&lt;li&gt;Large database responses&lt;/li&gt;
&lt;li&gt;Excessive serialization&lt;/li&gt;
&lt;li&gt;Synchronous operations&lt;/li&gt;
&lt;li&gt;External API calls inside request paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not simply to optimize individual queries.&lt;/p&gt;

&lt;p&gt;It is to understand the &lt;strong&gt;entire request lifecycle&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Your LCP Can Be Poor Even With Excellent TTFB
&lt;/h1&gt;

&lt;p&gt;This is one of the most important concepts for developers.&lt;/p&gt;

&lt;p&gt;Largest Contentful Paint measures when the largest visible image, text block, or video is rendered relative to the beginning of navigation. Google recommends an LCP of 2.5 seconds or less for a good user experience, measured at the 75th percentile. ([web.dev][3])&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TTFB = 400 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent.&lt;/p&gt;

&lt;p&gt;But then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTML arrives
    ↓
JavaScript executes
    ↓
Hero component mounts
    ↓
API request starts
    ↓
Hero image is discovered
    ↓
Image downloads
    ↓
Image decodes
    ↓
Hero finally renders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your server was fast.&lt;/p&gt;

&lt;p&gt;Your LCP was not.&lt;/p&gt;

&lt;p&gt;This is why performance optimization needs to follow the user's experience rather than a single backend metric.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Measure Real Users, Not Just Your Laptop
&lt;/h1&gt;

&lt;p&gt;A common developer mistake is testing a website on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A powerful desktop&lt;/li&gt;
&lt;li&gt;High-speed Wi-Fi&lt;/li&gt;
&lt;li&gt;Cached resources&lt;/li&gt;
&lt;li&gt;Local development environment&lt;/li&gt;
&lt;li&gt;Minimal browser extensions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then concluding:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The website is fast."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Real users may have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Older phones&lt;/li&gt;
&lt;li&gt;Slower CPUs&lt;/li&gt;
&lt;li&gt;Weak mobile networks&lt;/li&gt;
&lt;li&gt;Cold caches&lt;/li&gt;
&lt;li&gt;Different geographic locations&lt;/li&gt;
&lt;li&gt;Battery-saving modes&lt;/li&gt;
&lt;li&gt;Different browsers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why field data matters.&lt;/p&gt;

&lt;p&gt;Chrome's Real User Measurement ecosystem, including the Chrome User Experience Report, can provide real-world performance information. Lab tools such as Lighthouse and Chrome DevTools are useful for controlled testing and debugging. web.dev recommends combining lab and field measurements because certain metrics, particularly INP and CLS, can behave differently in real user environments. ([web.dev][4])&lt;/p&gt;




&lt;h1&gt;
  
  
  A Practical Performance Debugging Workflow
&lt;/h1&gt;

&lt;p&gt;Instead of randomly compressing files and rewriting code, developers should follow a structured process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Measure
&lt;/h3&gt;

&lt;p&gt;Start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome DevTools&lt;/li&gt;
&lt;li&gt;Lighthouse&lt;/li&gt;
&lt;li&gt;PageSpeed Insights&lt;/li&gt;
&lt;li&gt;Search Console Core Web Vitals&lt;/li&gt;
&lt;li&gt;Real User Monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google recommends using Core Web Vitals and related page experience tools to understand how users actually experience a site. ([Google for Developers][5])&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Identify the bottleneck
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is TTFB high?
Is LCP high?
Is JavaScript blocking the main thread?
Are images too large?
Are there too many requests?
Are third-party scripts involved?
Are API calls sequential?
Is the browser doing unnecessary work?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Fix the largest bottleneck first
&lt;/h3&gt;

&lt;p&gt;Do not spend three hours optimizing a 20 KB CSS file when a 4 MB hero image is causing the problem.&lt;/p&gt;

&lt;p&gt;Prioritize the changes that have the largest impact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Test again
&lt;/h3&gt;

&lt;p&gt;Performance optimization is an iterative process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measure
   ↓
Diagnose
   ↓
Optimize
   ↓
Measure again
   ↓
Compare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never assume an optimization worked simply because the code looks cleaner.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Fast Server Is Only the Beginning
&lt;/h1&gt;

&lt;p&gt;Website performance is not a competition between frontend and backend developers.&lt;/p&gt;

&lt;p&gt;It is a system.&lt;/p&gt;

&lt;p&gt;A fast server helps reduce TTFB. But after the response arrives, the browser still has to process everything you send it.&lt;/p&gt;

&lt;p&gt;That includes JavaScript, CSS, images, fonts, API responses, third-party scripts, animations, and DOM updates.&lt;/p&gt;

&lt;p&gt;The modern performance mindset should therefore be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do less work, send fewer bytes, make fewer requests, render important content sooner, and measure what real users experience.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Core Web Vitals provide useful targets for the user experience: LCP at or below 2.5 seconds, INP below 200 milliseconds, and CLS below 0.1 are Google's current "good" thresholds. ([Google for Developers][1])&lt;/p&gt;

&lt;p&gt;And performance is not only about SEO. Google explicitly recommends good Core Web Vitals and overall page experience because they contribute to a better experience for users, while also making clear that strong Core Web Vitals alone do not guarantee higher search rankings. ([Google for Developers][5])&lt;/p&gt;

&lt;p&gt;So the next time someone says:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Our server is fast. Why is the website still slow?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do not immediately look at the server.&lt;/p&gt;

&lt;p&gt;Open DevTools.&lt;/p&gt;

&lt;p&gt;Look at the network waterfall.&lt;/p&gt;

&lt;p&gt;Inspect the main thread.&lt;/p&gt;

&lt;p&gt;Check the LCP element.&lt;/p&gt;

&lt;p&gt;Audit JavaScript.&lt;/p&gt;

&lt;p&gt;Inspect images.&lt;/p&gt;

&lt;p&gt;Review API calls.&lt;/p&gt;

&lt;p&gt;Measure real users.&lt;/p&gt;

&lt;p&gt;Because &lt;strong&gt;website performance is not determined by how quickly your server responds. It is determined by how quickly the user can actually see, understand, and interact with your page.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Performance Checklist
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Check TTFB
[ ] Check LCP
[ ] Check INP
[ ] Check CLS
[ ] Analyze JavaScript execution
[ ] Remove unused JavaScript
[ ] Optimize images
[ ] Review render-blocking resources
[ ] Audit third-party scripts
[ ] Reduce unnecessary API requests
[ ] Check database queries
[ ] Implement effective caching
[ ] Test mobile performance
[ ] Test with cold cache
[ ] Compare lab and real-user data
[ ] Measure again after every major optimization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AI Agents vs Chatbots: Why the Difference Matters</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:24:38 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/ai-agents-vs-chatbots-why-the-difference-matters-8o8</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/ai-agents-vs-chatbots-why-the-difference-matters-8o8</guid>
      <description>&lt;p&gt;&lt;em&gt;AI chatbots answer questions. AI agents can reason about goals, use tools, execute actions, and adapt their next steps. Understanding the difference matters when building real AI applications.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Artificial intelligence has moved quickly from simple chat interfaces to systems that can interact with software, retrieve information, call APIs, execute workflows, and make decisions within defined boundaries.&lt;/p&gt;

&lt;p&gt;That shift has created a lot of confusion around one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is the actual difference between an AI agent and a chatbot?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The two terms are sometimes used interchangeably, but they describe different levels of system behavior.&lt;/p&gt;

&lt;p&gt;A chatbot can have memory, retrieval, tools, and sophisticated reasoning. An AI agent can also have all of these capabilities. The important distinction is not whether a system has a chat interface or uses an LLM.&lt;/p&gt;

&lt;p&gt;The more useful distinction is &lt;strong&gt;what the system is designed and authorized to do after receiving a request&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A research review published in 2026 describes an AI agent through an action loop involving goals, planning, tool use, observations, revisions, and outputs. Google Cloud similarly describes agents as applications that process inputs, reason with available tools, and take actions toward a goal. ([PubMed Central (PMC)][1])&lt;/p&gt;

&lt;p&gt;This difference has major implications for developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Chatbot?
&lt;/h2&gt;

&lt;p&gt;A chatbot is a software application designed primarily to communicate with users through conversation.&lt;/p&gt;

&lt;p&gt;A simplified architecture looks 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;User
  ↓
Message
  ↓
AI Model
  ↓
Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User:
Explain REST APIs.

Chatbot:
A REST API is an application programming interface
that follows REST architectural principles...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system receives an input and generates an appropriate response.&lt;/p&gt;

&lt;p&gt;Modern chatbots can be much more sophisticated than this example. They may use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large language models&lt;/li&gt;
&lt;li&gt;Retrieval-augmented generation&lt;/li&gt;
&lt;li&gt;Conversation history&lt;/li&gt;
&lt;li&gt;Knowledge bases&lt;/li&gt;
&lt;li&gt;Tool calling&lt;/li&gt;
&lt;li&gt;File analysis&lt;/li&gt;
&lt;li&gt;Web search&lt;/li&gt;
&lt;li&gt;Structured outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, &lt;strong&gt;a chatbot is not necessarily a simple system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The important point is that conversation is generally the primary interaction model.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Is an AI Agent?
&lt;/h1&gt;

&lt;p&gt;An AI agent is a system designed to pursue a goal by reasoning about tasks, using available tools, taking actions, observing results, and deciding what to do next.&lt;/p&gt;

&lt;p&gt;A simplified architecture looks 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;User Goal
   ↓
AI Model
   ↓
Plan
   ↓
Select Tool
   ↓
Execute Action
   ↓
Observe Result
   ↓
Evaluate
   ↓
Next Action
   ↓
Final Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google Cloud identifies several major components of agentic architectures, including the model, grounding, tools, memory, orchestration, and runtime. ([Google Cloud][2])&lt;/p&gt;

&lt;p&gt;For example, imagine a developer says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Investigate why our API is returning 500 errors and prepare a report."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A conventional chatbot might explain common causes of HTTP 500 errors.&lt;/p&gt;

&lt;p&gt;An agent could potentially:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect authorized logs&lt;/li&gt;
&lt;li&gt;Identify recent errors&lt;/li&gt;
&lt;li&gt;Search the relevant code&lt;/li&gt;
&lt;li&gt;Compare recent deployments&lt;/li&gt;
&lt;li&gt;Run approved diagnostic commands&lt;/li&gt;
&lt;li&gt;Identify a likely cause&lt;/li&gt;
&lt;li&gt;Test a hypothesis&lt;/li&gt;
&lt;li&gt;Produce a report&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The important difference is that the agent is participating in the &lt;strong&gt;workflow&lt;/strong&gt;, not simply explaining the workflow.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Core Difference: Response vs Goal
&lt;/h1&gt;

&lt;p&gt;The simplest way to think about the difference is:&lt;/p&gt;

&lt;h3&gt;
  
  
  Chatbot
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"Give me an answer."&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Agent
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"Achieve this goal."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A chatbot generally focuses on generating a response to the current interaction.&lt;/p&gt;

&lt;p&gt;An agent focuses on accomplishing an objective through one or more steps.&lt;/p&gt;

&lt;p&gt;This distinction is more useful than simply asking whether the application uses GPT, Gemini, Claude, or another model.&lt;/p&gt;

&lt;p&gt;A system can use a highly capable LLM and still function primarily as a chatbot.&lt;/p&gt;

&lt;p&gt;Likewise, an agent can use an LLM as only one component of a much larger software architecture.&lt;/p&gt;




&lt;h1&gt;
  
  
  AI Agents vs Chatbots: A Technical Comparison
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Chatbot&lt;/th&gt;
&lt;th&gt;AI Agent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Conversational interface&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generates text&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses conversation context&lt;/td&gt;
&lt;td&gt;Usually&lt;/td&gt;
&lt;td&gt;Usually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieves information&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;td&gt;Often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses external tools&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;td&gt;Core capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-step planning&lt;/td&gt;
&lt;td&gt;Limited or application-dependent&lt;/td&gt;
&lt;td&gt;Core capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Executes actions&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Often&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observes tool results&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintains task state&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;td&gt;Common&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autonomous workflow&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Core use case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human approval&lt;/td&gt;
&lt;td&gt;Common&lt;/td&gt;
&lt;td&gt;Important for sensitive actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Goal-oriented execution&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Central concept&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This table should not be interpreted as a strict technical taxonomy. There is significant overlap between modern chatbot and agent architectures.&lt;/p&gt;

&lt;p&gt;The distinction is primarily about &lt;strong&gt;system behavior and architecture&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Tools Change Everything
&lt;/h1&gt;

&lt;p&gt;One of the biggest differences between a chatbot and an agent is tool access.&lt;/p&gt;

&lt;p&gt;An LLM can generate a response.&lt;/p&gt;

&lt;p&gt;A tool gives the AI a way to interact with something outside the model.&lt;/p&gt;

&lt;p&gt;Tools can include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Web Search
Database Query
REST API
Code Execution
File System
CRM
Email
Calendar
Cloud Infrastructure
Analytics Platform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google Cloud describes tools as functions or APIs that allow an agent to interact with external systems and data sources. It also notes that tools transform an AI model from a text generator into a system capable of automating complex, multi-step tasks. ([Google Cloud Documentation][3])&lt;/p&gt;

&lt;p&gt;Consider a customer support application.&lt;/p&gt;

&lt;p&gt;A chatbot might answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Your order appears to be delayed."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An agent with authorized access could:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Identify the customer
2. Query the order database
3. Check shipping status
4. Retrieve carrier information
5. Determine the issue
6. Draft an appropriate response
7. Escalate the case if necessary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is still responsible for reasoning and language, but the surrounding application gives it capabilities.&lt;/p&gt;




&lt;h1&gt;
  
  
  Planning Is Another Major Difference
&lt;/h1&gt;

&lt;p&gt;Chatbots often operate one interaction at a time.&lt;/p&gt;

&lt;p&gt;Agents can decompose a goal into multiple tasks.&lt;/p&gt;

&lt;p&gt;Suppose the goal is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Research three competitors and create a comparison."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An agent might create an internal plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Goal:
Create competitor comparison

Step 1:
Identify competitors

Step 2:
Collect product information

Step 3:
Collect pricing information

Step 4:
Compare features

Step 5:
Evaluate positioning

Step 6:
Organize findings

Step 7:
Generate report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google Cloud's agent architecture documentation describes agents as systems that can understand user intent, create multi-step plans, and execute those plans using available tools. ([Google Cloud Documentation][3])&lt;/p&gt;

&lt;p&gt;Planning does not necessarily mean that an agent creates a visible checklist.&lt;/p&gt;

&lt;p&gt;The planning process may happen internally through model reasoning, orchestration code, workflows, or a combination of these approaches.&lt;/p&gt;




&lt;h1&gt;
  
  
  Memory: Why Agents Need State
&lt;/h1&gt;

&lt;p&gt;Another important concept is memory.&lt;/p&gt;

&lt;p&gt;Imagine an agent working on a software project for several hours.&lt;/p&gt;

&lt;p&gt;It may need to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Previous decisions&lt;/li&gt;
&lt;li&gt;Files it inspected&lt;/li&gt;
&lt;li&gt;Tool results&lt;/li&gt;
&lt;li&gt;User preferences&lt;/li&gt;
&lt;li&gt;Current task state&lt;/li&gt;
&lt;li&gt;Errors encountered&lt;/li&gt;
&lt;li&gt;Completed steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google's current agent architecture guidance distinguishes short-term memory, which maintains context within a session, from long-term memory, which can persist relevant information across interactions. ([Google Cloud Documentation][3])&lt;/p&gt;

&lt;p&gt;A simple chatbot may only need conversation history.&lt;/p&gt;

&lt;p&gt;An agent often needs &lt;strong&gt;state management&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For 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;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debug_api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"investigating"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files_checked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"routes.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errors_found"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"next_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inspect_database_connection"&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;This state can be stored outside the model.&lt;/p&gt;

&lt;p&gt;That is an important architectural point:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Memory is not the same thing as model intelligence.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The application must deliberately design how information is stored, retrieved, updated, isolated, and deleted.&lt;/p&gt;




&lt;h1&gt;
  
  
  RAG Is Not the Same as an AI Agent
&lt;/h1&gt;

&lt;p&gt;Another common source of confusion is &lt;strong&gt;Retrieval-Augmented Generation&lt;/strong&gt;, or RAG.&lt;/p&gt;

&lt;p&gt;A typical RAG workflow looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Question
     ↓
Search Knowledge Base
     ↓
Retrieve Relevant Documents
     ↓
Send Context to Model
     ↓
Generate Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An agentic workflow can be more dynamic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Goal
     ↓
Determine Required Information
     ↓
Search Knowledge Base
     ↓
Call API
     ↓
Analyze Result
     ↓
Search Again
     ↓
Execute Tool
     ↓
Evaluate Outcome
     ↓
Return Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
RAG can therefore be one component inside an agent.&lt;/p&gt;

&lt;p&gt;Google Cloud explicitly distinguishes agentic architectures from non-agentic approaches such as direct model reasoning and RAG, noting that agents can dynamically decide which tools and information to use during a multi-step task. ([Google Cloud Documentation][4])&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RAG is a retrieval technique.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An agent is a goal-oriented system architecture.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They can work together.&lt;/p&gt;


&lt;h1&gt;
  
  
  Where Chatbots Still Make Sense
&lt;/h1&gt;

&lt;p&gt;Not every AI application needs to become an agent.&lt;/p&gt;

&lt;p&gt;This is an important point for developers.&lt;/p&gt;

&lt;p&gt;If the task is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Summarizing text&lt;/li&gt;
&lt;li&gt;Translating content&lt;/li&gt;
&lt;li&gt;Explaining a concept&lt;/li&gt;
&lt;li&gt;Answering questions from a document&lt;/li&gt;
&lt;li&gt;Generating an email draft&lt;/li&gt;
&lt;li&gt;Classifying customer feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple AI application may be sufficient.&lt;/p&gt;

&lt;p&gt;Google Cloud specifically notes that deterministic tasks such as summarization, translation, and classification do not necessarily require an agentic workflow and that other approaches can be more efficient and cost-effective. ([Google Cloud Documentation][3])&lt;/p&gt;

&lt;p&gt;Adding autonomous planning and tool use to a simple task can increase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complexity&lt;/li&gt;
&lt;li&gt;Latency&lt;/li&gt;
&lt;li&gt;Cost&lt;/li&gt;
&lt;li&gt;Failure modes&lt;/li&gt;
&lt;li&gt;Security requirements&lt;/li&gt;
&lt;li&gt;Testing requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engineering goal should not be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use an agent because agents are more advanced."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use the simplest architecture that reliably solves the problem."&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  Where AI Agents Make Sense
&lt;/h1&gt;

&lt;p&gt;Agents become more useful when a task is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-step&lt;/li&gt;
&lt;li&gt;Open-ended&lt;/li&gt;
&lt;li&gt;Tool-dependent&lt;/li&gt;
&lt;li&gt;Goal-oriented&lt;/li&gt;
&lt;li&gt;Dynamic&lt;/li&gt;
&lt;li&gt;Difficult to represent with fixed rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples include:&lt;/p&gt;
&lt;h3&gt;
  
  
  Coding Agents
&lt;/h3&gt;

&lt;p&gt;A coding agent may inspect a repository, modify files, execute tests, analyze errors, and iterate.&lt;/p&gt;
&lt;h3&gt;
  
  
  Research Agents
&lt;/h3&gt;

&lt;p&gt;A research agent may search multiple sources, collect information, compare findings, and produce a structured report.&lt;/p&gt;
&lt;h3&gt;
  
  
  Customer Support Agents
&lt;/h3&gt;

&lt;p&gt;A support agent may retrieve account information, investigate an issue, update records, and escalate cases.&lt;/p&gt;
&lt;h3&gt;
  
  
  IT Operations Agents
&lt;/h3&gt;

&lt;p&gt;An operations agent may analyze alerts, inspect logs, investigate infrastructure problems, and perform approved remediation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Business Workflow Agents
&lt;/h3&gt;

&lt;p&gt;An agent may coordinate information across CRM, email, analytics, databases, and internal applications.&lt;/p&gt;

&lt;p&gt;The common pattern is not the industry.&lt;/p&gt;

&lt;p&gt;It is the &lt;strong&gt;workflow&lt;/strong&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Security Problem Changes Too
&lt;/h1&gt;

&lt;p&gt;Giving an AI system the ability to take actions also increases its attack surface.&lt;/p&gt;

&lt;p&gt;OWASP identifies risks for agentic systems including prompt injection, tool abuse, privilege escalation, data exfiltration, memory poisoning, goal hijacking, excessive autonomy, and cascading failures. ([OWASP Cheat Sheet Series][5])&lt;/p&gt;

&lt;p&gt;Consider this scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent
  ↓
Reads webpage
  ↓
Webpage contains malicious instructions
  ↓
Agent interprets instructions as relevant
  ↓
Agent calls a privileged tool
  ↓
Unexpected action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one reason developers should treat external information as untrusted input.&lt;/p&gt;

&lt;p&gt;OWASP recommends least-privilege tool access, input validation, prompt injection defenses, memory protection, and structured testing for agentic systems. ([OWASP Cheat Sheet Series][5])&lt;/p&gt;

&lt;p&gt;OWASP also identifies &lt;strong&gt;excessive agency&lt;/strong&gt; as a vulnerability when an LLM-based system has enough permissions to perform damaging actions in response to unexpected, ambiguous, or manipulated model outputs. ([OWASP Gen AI Security Project][6])&lt;/p&gt;

&lt;p&gt;This leads to a fundamental engineering principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;An agent should have only the permissions it actually needs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Human-in-the-Loop Still Matters
&lt;/h1&gt;

&lt;p&gt;Autonomy does not mean removing humans from every workflow.&lt;/p&gt;

&lt;p&gt;For high-impact actions, developers can require approval.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Agent
   ↓
Generate database update
   ↓
Validation
   ↓
Human Approval
   ↓
Execute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be especially useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Financial transactions&lt;/li&gt;
&lt;li&gt;Production deployments&lt;/li&gt;
&lt;li&gt;Deleting data&lt;/li&gt;
&lt;li&gt;Sending external communications&lt;/li&gt;
&lt;li&gt;Changing permissions&lt;/li&gt;
&lt;li&gt;Legal or compliance workflows&lt;/li&gt;
&lt;li&gt;Administrative actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A well-designed agent is not necessarily one that acts without humans.&lt;/p&gt;

&lt;p&gt;It is one that has &lt;strong&gt;clearly defined boundaries around when it can act and when it must ask for help.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simple Agent Architecture for Developers
&lt;/h1&gt;

&lt;p&gt;A basic implementation can be represented as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌──────────────┐
                    │     User     │
                    └──────┬───────┘
                           ↓
                    ┌──────────────┐
                    │ Agent Runtime│
                    └──────┬───────┘
                           ↓
                    ┌──────────────┐
                    │  AI Model    │
                    └──────┬───────┘
                           ↓
                 ┌─────────┴─────────┐
                 ↓                   ↓
            ┌─────────┐         ┌─────────┐
            │ Memory  │         │  Tools  │
            └─────────┘         └────┬────┘
                                     ↓
                              External Systems
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production, additional layers may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Guardrails&lt;/li&gt;
&lt;li&gt;Audit logs&lt;/li&gt;
&lt;li&gt;Human approval&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Cost controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture should grow according to the task rather than adding components simply because they are available.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Real Shift: From Generating Text to Operating Software
&lt;/h1&gt;

&lt;p&gt;This is why the chatbot versus agent distinction matters.&lt;/p&gt;

&lt;p&gt;A chatbot primarily gives you an interface to an AI model.&lt;/p&gt;

&lt;p&gt;An agent gives the model a &lt;strong&gt;runtime environment in which it can pursue a goal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That environment can contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model
+
Context
+
Memory
+
Tools
+
Data
+
Orchestration
+
Permissions
+
Evaluation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Google Cloud's current architecture guidance describes these components as part of the broader agentic application architecture. ([Google Cloud Documentation][3])&lt;/p&gt;

&lt;p&gt;This means the future of AI development is not simply about choosing a smarter model.&lt;/p&gt;

&lt;p&gt;Developers increasingly need to think about the entire system surrounding the model.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;AI agents and chatbots are closely related, but they are not interchangeable concepts.&lt;/p&gt;

&lt;p&gt;A chatbot is primarily designed around &lt;strong&gt;conversation and response generation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An AI agent is designed around &lt;strong&gt;goals, reasoning, tools, actions, state, and feedback&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The difference can be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chatbot

Input
  ↓
Reason
  ↓
Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Agent

Goal
  ↓
Reason
  ↓
Plan
  ↓
Use Tools
  ↓
Act
  ↓
Observe
  ↓
Evaluate
  ↓
Repeat or Finish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither architecture is universally better.&lt;/p&gt;

&lt;p&gt;For simple, predictable tasks, a chatbot or direct AI workflow can be easier to build, test, operate, and secure.&lt;/p&gt;

&lt;p&gt;For complex workflows requiring multiple steps, external data, and actions, an agentic architecture can provide capabilities that a conventional chatbot does not.&lt;/p&gt;

&lt;p&gt;The most important question for developers is therefore not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Should I build an AI agent?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Does this problem actually require an AI system to plan, use tools, and take actions?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is yes, an agent may be the right architecture.&lt;/p&gt;

&lt;p&gt;If the answer is no, keeping the system simpler may be the better engineering decision.&lt;/p&gt;




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

&lt;p&gt;For developers who want to go deeper, the architecture and security documentation from Google Cloud and OWASP provide useful starting points for understanding agent runtimes, tools, memory, orchestration, permissions, and agent-specific threats. ([Google Cloud Documentation][3])&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suggested DEV tags:&lt;/strong&gt; &lt;code&gt;ai&lt;/code&gt; &lt;code&gt;artificialintelligence&lt;/code&gt; &lt;code&gt;agents&lt;/code&gt; &lt;code&gt;chatbots&lt;/code&gt; &lt;code&gt;programming&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suggested DEV title:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;AI Agents vs Chatbots: Why the Difference Matters&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>What Happens When You Turn One Lecture Into a Complete Study System?</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Wed, 30 Sep 2026 18:49:12 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/what-happens-when-you-turn-one-lecture-into-a-complete-study-system-2mm</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/what-happens-when-you-turn-one-lecture-into-a-complete-study-system-2mm</guid>
      <description>&lt;p&gt;A lecture is only the beginning. The real learning happens when information is transformed into understanding, retrieval, practice, and feedback.*&lt;/p&gt;

&lt;p&gt;A student attends a lecture.&lt;/p&gt;

&lt;p&gt;They download the slides.&lt;/p&gt;

&lt;p&gt;Maybe they also receive a PDF from the professor.&lt;/p&gt;

&lt;p&gt;Everything they need for the exam appears to be sitting in one folder.&lt;/p&gt;

&lt;p&gt;Then the week before the exam arrives.&lt;/p&gt;

&lt;p&gt;They open the lecture again.&lt;/p&gt;

&lt;p&gt;They reread the slides.&lt;/p&gt;

&lt;p&gt;They highlight important sections.&lt;/p&gt;

&lt;p&gt;They watch the recording again.&lt;/p&gt;

&lt;p&gt;Eventually, they take a practice test and discover that they cannot remember half of what they thought they knew.&lt;/p&gt;

&lt;p&gt;The problem may not be a lack of information.&lt;/p&gt;

&lt;p&gt;The problem is the workflow.&lt;/p&gt;

&lt;p&gt;Modern students often have more educational material than ever. Lectures, PDFs, slides, recordings, articles, textbooks, online videos, notes, AI explanations, and practice resources can all exist around the same course.&lt;/p&gt;

&lt;p&gt;But having information available does not automatically create a learning system.&lt;/p&gt;

&lt;p&gt;A better approach starts with one simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One source of information can become many connected learning activities.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A lecture can become an explanation.&lt;/p&gt;

&lt;p&gt;The explanation can become a study guide.&lt;/p&gt;

&lt;p&gt;The study guide can become questions.&lt;/p&gt;

&lt;p&gt;The questions can become retrieval practice.&lt;/p&gt;

&lt;p&gt;Retrieval can become spaced review.&lt;/p&gt;

&lt;p&gt;Review can lead to a practice exam.&lt;/p&gt;

&lt;p&gt;The practice exam can generate feedback.&lt;/p&gt;

&lt;p&gt;The result is not simply a collection of study materials.&lt;/p&gt;

&lt;p&gt;It is a &lt;strong&gt;learning pipeline&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Problem Is Not the Lecture
&lt;/h1&gt;

&lt;p&gt;Lectures are designed to introduce information.&lt;/p&gt;

&lt;p&gt;They are not necessarily designed to become a complete study system.&lt;/p&gt;

&lt;p&gt;Imagine a 60-minute lecture about the cardiovascular system.&lt;/p&gt;

&lt;p&gt;The student might receive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;45 presentation slides&lt;/li&gt;
&lt;li&gt;A lecture recording&lt;/li&gt;
&lt;li&gt;12 pages of notes&lt;/li&gt;
&lt;li&gt;Several diagrams&lt;/li&gt;
&lt;li&gt;A recommended textbook chapter&lt;/li&gt;
&lt;li&gt;A list of learning objectives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The student now has plenty of information.&lt;/p&gt;

&lt;p&gt;But what should happen next?&lt;/p&gt;

&lt;p&gt;Most students improvise.&lt;/p&gt;

&lt;p&gt;They reread.&lt;/p&gt;

&lt;p&gt;They highlight.&lt;/p&gt;

&lt;p&gt;They make summaries.&lt;/p&gt;

&lt;p&gt;They search for videos.&lt;/p&gt;

&lt;p&gt;They create random flashcards.&lt;/p&gt;

&lt;p&gt;They find old quizzes.&lt;/p&gt;

&lt;p&gt;Each activity might be useful on its own, but there is often no connection between them.&lt;/p&gt;

&lt;p&gt;This creates a fragmented workflow.&lt;/p&gt;

&lt;p&gt;A better system starts with the original source and deliberately transforms it through multiple stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lecture/PDF&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Study Guide&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spaced Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice Exam&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feedback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Targeted Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a fundamentally different way of thinking about studying.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Start With the Source
&lt;/h1&gt;

&lt;p&gt;The first stage is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capture the original material.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A lecture&lt;/li&gt;
&lt;li&gt;PDF&lt;/li&gt;
&lt;li&gt;Slide deck&lt;/li&gt;
&lt;li&gt;Textbook chapter&lt;/li&gt;
&lt;li&gt;Class notes&lt;/li&gt;
&lt;li&gt;Research paper&lt;/li&gt;
&lt;li&gt;Recorded lesson&lt;/li&gt;
&lt;li&gt;Course handout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The source matters because it provides context.&lt;/p&gt;

&lt;p&gt;A generic explanation of a topic may be accurate, but a student's course has its own terminology, examples, emphasis, and learning objectives.&lt;/p&gt;

&lt;p&gt;For example, two biology courses may teach the same topic but emphasize different mechanisms.&lt;/p&gt;

&lt;p&gt;One professor might focus heavily on molecular pathways.&lt;/p&gt;

&lt;p&gt;Another might emphasize clinical applications.&lt;/p&gt;

&lt;p&gt;The student's learning system should therefore remain connected to the material they are actually expected to learn.&lt;/p&gt;

&lt;p&gt;This is one reason source-based learning can be powerful.&lt;/p&gt;

&lt;p&gt;Instead of starting with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What does the internet say about this topic?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;the workflow begins with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What does my course material require me to understand?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That distinction can keep the rest of the study process focused.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Turn the Lecture Into Understanding
&lt;/h1&gt;

&lt;p&gt;A lecture is not knowledge simply because it has been attended.&lt;/p&gt;

&lt;p&gt;The first transformation should be from raw information into structure.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;What are the major concepts?&lt;/li&gt;
&lt;li&gt;What are the important definitions?&lt;/li&gt;
&lt;li&gt;What processes need to be understood?&lt;/li&gt;
&lt;li&gt;Which concepts are connected?&lt;/li&gt;
&lt;li&gt;What examples explain the ideas?&lt;/li&gt;
&lt;li&gt;What could easily be confused?&lt;/li&gt;
&lt;li&gt;What questions should a student be able to answer after studying this?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a conceptual map of the material.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Photosynthesis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Light-dependent reactions&lt;br&gt;
→ ATP and NADPH&lt;br&gt;
→ Calvin cycle&lt;br&gt;
→ Carbon fixation&lt;br&gt;
→ Glucose production&lt;/p&gt;

&lt;p&gt;Now the student has more than a collection of paragraphs.&lt;/p&gt;

&lt;p&gt;They have a structure that can support later learning.&lt;/p&gt;

&lt;p&gt;Research on learning strategies consistently emphasizes active processes such as retrieval, spacing, elaboration, and organization rather than relying entirely on passive rereading. ([Springer][1])&lt;/p&gt;

&lt;p&gt;The purpose of this stage is not to memorize.&lt;/p&gt;

&lt;p&gt;It is to make the material understandable enough that it can later be retrieved and applied.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Build a Study Guide
&lt;/h1&gt;

&lt;p&gt;Once the material is understood at a basic level, the next step is compression with structure.&lt;/p&gt;

&lt;p&gt;A good study guide should answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do I actually need to know?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of keeping the lecture as a 60-slide presentation, transform it into something like:&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Concepts
&lt;/h3&gt;

&lt;p&gt;The five ideas that define the topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Important Terms
&lt;/h3&gt;

&lt;p&gt;Definitions students need to understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Processes
&lt;/h3&gt;

&lt;p&gt;Steps, mechanisms, or sequences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relationships
&lt;/h3&gt;

&lt;p&gt;Cause and effect, comparisons, dependencies, and connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Confusions
&lt;/h3&gt;

&lt;p&gt;Ideas that students are likely to mix up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Questions
&lt;/h3&gt;

&lt;p&gt;Things the student should eventually be able to answer without looking.&lt;/p&gt;

&lt;p&gt;This is more useful than simply shortening the lecture.&lt;/p&gt;

&lt;p&gt;The objective is to create a map from which future learning activities can be generated.&lt;/p&gt;

&lt;p&gt;And that leads to an important distinction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A study guide should not be the final product. It should be the starting point for practice.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  4. Turn Information Into Questions
&lt;/h1&gt;

&lt;p&gt;This is where the workflow changes from consumption to active learning.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What should I reread?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What should I be able to answer?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Take a statement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The mitochondria produce ATP through cellular respiration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Turn it into questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the role of mitochondria in cellular respiration?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How is ATP produced?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens during oxidative phosphorylation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is oxygen important?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would happen if oxygen were unavailable?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now the student has potential retrieval opportunities.&lt;/p&gt;

&lt;p&gt;This matters because questions force the learner to produce knowledge.&lt;/p&gt;

&lt;p&gt;A systematic review of classroom research on retrieval practice examined 50 experiments involving 5,374 participants. The authors reported that 57% of the observed effect sizes represented medium or large benefits, with benefits appearing across different educational levels, subject areas, testing formats, and timing conditions. The authors also noted that only 6% of the experiments were conducted in non-WEIRD countries, which is an important limitation when generalizing the evidence globally. ([DOI][2])&lt;/p&gt;

&lt;p&gt;The lesson is not that every question automatically improves learning.&lt;/p&gt;

&lt;p&gt;It is that &lt;strong&gt;retrieval deserves a deliberate place in the study workflow&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Retrieval Is Where the Student Has to Do the Work
&lt;/h1&gt;

&lt;p&gt;This may be the most important transformation in the entire pipeline.&lt;/p&gt;

&lt;p&gt;The student reads the study guide.&lt;/p&gt;

&lt;p&gt;Then the guide disappears.&lt;/p&gt;

&lt;p&gt;Now they try to answer the questions.&lt;/p&gt;

&lt;p&gt;That moment is different.&lt;/p&gt;

&lt;p&gt;There is no visible answer to recognize.&lt;/p&gt;

&lt;p&gt;The student has to search memory.&lt;/p&gt;

&lt;p&gt;Maybe they remember everything.&lt;/p&gt;

&lt;p&gt;Maybe they remember part of it.&lt;/p&gt;

&lt;p&gt;Maybe they realize they understood far less than they thought.&lt;/p&gt;

&lt;p&gt;All three outcomes provide useful information.&lt;/p&gt;

&lt;p&gt;Retrieval practice is not simply a way of measuring learning after it happens.&lt;/p&gt;

&lt;p&gt;It can also contribute to learning itself. Research reviews have examined how practicing retrieval improves the ability to retrieve information again later. ([Annual Reviews][3])&lt;/p&gt;

&lt;p&gt;This is why a strong study system should repeatedly move students from:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read → Retrieve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;rather than:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read → Read again → Read again&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The difference may feel small.&lt;/p&gt;

&lt;p&gt;The learning process is not.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Add Spaced Review
&lt;/h1&gt;

&lt;p&gt;One lecture should not become one study session.&lt;/p&gt;

&lt;p&gt;The student should encounter the important material again over time.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Day 1:&lt;/strong&gt; Learn the material&lt;br&gt;
&lt;strong&gt;Day 2:&lt;/strong&gt; Short retrieval session&lt;br&gt;
&lt;strong&gt;Day 5:&lt;/strong&gt; Review difficult questions&lt;br&gt;
&lt;strong&gt;Day 9:&lt;/strong&gt; Mixed retrieval&lt;br&gt;
&lt;strong&gt;Day 15:&lt;/strong&gt; Practice exam&lt;br&gt;
&lt;strong&gt;Day 25:&lt;/strong&gt; Final review&lt;/p&gt;

&lt;p&gt;The exact schedule can vary.&lt;/p&gt;

&lt;p&gt;The principle is what matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spread learning opportunities across time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A major review in &lt;em&gt;Nature Reviews Psychology&lt;/em&gt; identifies spacing and retrieval practice as two well-supported strategies for improving learning across different domains and stages of life. ([DOI][4])&lt;/p&gt;

&lt;p&gt;A systematic review in health professions education also examined 56 studies containing 63 experiments. Forty-three experiments demonstrated significant benefits for distributed practice and/or retrieval practice compared with control or comparison conditions, although the studies varied considerably in design and context. ([Springer][5])&lt;/p&gt;

&lt;p&gt;This means the lecture should not disappear after the first study session.&lt;/p&gt;

&lt;p&gt;It should become the source of future retrieval opportunities.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Turn the Same Material Into a Practice Exam
&lt;/h1&gt;

&lt;p&gt;Eventually, the student needs to move beyond individual questions.&lt;/p&gt;

&lt;p&gt;They need to simulate the situation where the information must be used under constraints.&lt;/p&gt;

&lt;p&gt;That means a practice exam.&lt;/p&gt;

&lt;p&gt;A good practice exam can combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recall&lt;/li&gt;
&lt;li&gt;Application&lt;/li&gt;
&lt;li&gt;Problem solving&lt;/li&gt;
&lt;li&gt;Comparisons&lt;/li&gt;
&lt;li&gt;Scenario-based questions&lt;/li&gt;
&lt;li&gt;Multiple concepts&lt;/li&gt;
&lt;li&gt;Different levels of difficulty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The purpose is not simply to generate another document.&lt;/p&gt;

&lt;p&gt;It is to create a realistic test of whether the learning pipeline worked.&lt;/p&gt;

&lt;p&gt;A student may perform well on isolated flashcards but struggle when several concepts appear together.&lt;/p&gt;

&lt;p&gt;That is useful information.&lt;/p&gt;

&lt;p&gt;It reveals a gap between &lt;strong&gt;recognition&lt;/strong&gt; and &lt;strong&gt;application&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Research on learning techniques has repeatedly highlighted practice testing and distributed practice as important evidence-based approaches. A 2021 meta-analysis of ten learning techniques, based on 242 studies and 169,179 participants, identified distributed practice and practice testing among the strongest techniques examined in that analysis. ([Frontiers][6])&lt;/p&gt;

&lt;p&gt;The practice exam therefore becomes a diagnostic tool.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Feedback Closes the Loop
&lt;/h1&gt;

&lt;p&gt;A practice exam without feedback only tells you that something went wrong.&lt;/p&gt;

&lt;p&gt;Feedback explains what happened.&lt;/p&gt;

&lt;p&gt;Suppose a student gets five questions wrong.&lt;/p&gt;

&lt;p&gt;The useful next question is not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How many did I get wrong?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Why did I get them wrong?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe the student:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forgot a definition&lt;/li&gt;
&lt;li&gt;Misunderstood a concept&lt;/li&gt;
&lt;li&gt;Confused two similar ideas&lt;/li&gt;
&lt;li&gt;Applied the wrong formula&lt;/li&gt;
&lt;li&gt;Misread the question&lt;/li&gt;
&lt;li&gt;Knew the information but could not retrieve it&lt;/li&gt;
&lt;li&gt;Could retrieve the information but could not apply it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each problem requires a different response.&lt;/p&gt;

&lt;p&gt;If the student forgot a definition, retrieval practice may help.&lt;/p&gt;

&lt;p&gt;If the student misunderstood the concept, they need another explanation.&lt;/p&gt;

&lt;p&gt;If they cannot apply the concept, they need more problem solving.&lt;/p&gt;

&lt;p&gt;This is where the learning pipeline becomes adaptive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test → Diagnose → Target the weakness → Retrieve again&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The objective is not simply to generate a score.&lt;/p&gt;

&lt;p&gt;It is to generate useful information about what happens next.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Lecture Can Become a Complete Learning System
&lt;/h1&gt;

&lt;p&gt;Now consider what happened to that original 60-minute lecture.&lt;/p&gt;

&lt;p&gt;It started as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One lecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then became:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A structured study guide&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A set of questions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval practice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spaced review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A practice exam&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feedback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Targeted revision&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The original source has not changed.&lt;/p&gt;

&lt;p&gt;The student's interaction with it has.&lt;/p&gt;

&lt;p&gt;That is the important idea.&lt;/p&gt;

&lt;p&gt;The value of educational technology should not simply be measured by how quickly it creates content.&lt;/p&gt;

&lt;p&gt;It should also be measured by whether it helps students move through the right learning activities.&lt;/p&gt;




&lt;h1&gt;
  
  
  AI Can Make the Transformation Easier
&lt;/h1&gt;

&lt;p&gt;This is where generative AI becomes particularly interesting.&lt;/p&gt;

&lt;p&gt;AI can help transform a source into different formats much faster than a student can manually create everything.&lt;/p&gt;

&lt;p&gt;A PDF can become a structured study guide.&lt;/p&gt;

&lt;p&gt;A study guide can generate questions.&lt;/p&gt;

&lt;p&gt;Questions can become flashcards.&lt;/p&gt;

&lt;p&gt;The same source can produce practice quizzes.&lt;/p&gt;

&lt;p&gt;A complicated section can become a visual explanation.&lt;/p&gt;

&lt;p&gt;A difficult concept can be explained at different levels.&lt;/p&gt;

&lt;p&gt;A practice exam can reveal weak areas.&lt;/p&gt;

&lt;p&gt;The technology can therefore reduce the friction between stages.&lt;/p&gt;

&lt;p&gt;But there is an important distinction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automation should reduce preparation work, not eliminate learning work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If AI creates a 20-page summary and the student simply reads it, the workflow may still be passive.&lt;/p&gt;

&lt;p&gt;If AI uses the source to create questions that the student must answer, the technology is supporting retrieval.&lt;/p&gt;

&lt;p&gt;If it generates a practice exam and explains mistakes after the student attempts it, the system becomes more interactive.&lt;/p&gt;

&lt;p&gt;The difference is not the AI model.&lt;/p&gt;

&lt;p&gt;The difference is the &lt;strong&gt;workflow around the model&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  One Source, Multiple Learning Modes
&lt;/h1&gt;

&lt;p&gt;A particularly useful principle is that students should not have to start from scratch for every study activity.&lt;/p&gt;

&lt;p&gt;The same source can support multiple modes:&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading
&lt;/h3&gt;

&lt;p&gt;Understand the original material.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structured learning
&lt;/h3&gt;

&lt;p&gt;Create a study guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieval
&lt;/h3&gt;

&lt;p&gt;Answer questions without looking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual learning
&lt;/h3&gt;

&lt;p&gt;Represent difficult relationships or processes visually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Audio learning
&lt;/h3&gt;

&lt;p&gt;Review concepts through spoken explanations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practice
&lt;/h3&gt;

&lt;p&gt;Solve questions and problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assessment
&lt;/h3&gt;

&lt;p&gt;Take a practice exam.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feedback
&lt;/h3&gt;

&lt;p&gt;Identify weaknesses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review
&lt;/h3&gt;

&lt;p&gt;Return to weak material later.&lt;/p&gt;

&lt;p&gt;This creates continuity.&lt;/p&gt;

&lt;p&gt;Instead of having a lecture in one application, flashcards in another, practice questions somewhere else, and notes in a fourth location, the learning activities can remain connected to the same underlying source.&lt;br&gt;
&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
That connection matters because context is preserved.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Real Bottleneck Is Workflow
&lt;/h1&gt;

&lt;p&gt;Students do not necessarily need more educational content.&lt;/p&gt;

&lt;p&gt;They need better ways to move through the content they already have.&lt;/p&gt;

&lt;p&gt;Consider the difference:&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional workflow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Lecture → Notes → Reread → Exam&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;versus:&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning pipeline
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Source → Understand → Organize → Question → Retrieve → Space → Practice → Feedback → Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The second workflow contains more opportunities to discover problems before the final exam.&lt;/p&gt;

&lt;p&gt;That is important.&lt;/p&gt;

&lt;p&gt;The exam should not be the first time a student discovers that they do not understand the material.&lt;/p&gt;

&lt;p&gt;A good learning system creates smaller feedback loops along the way.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Pipeline Should Be Designed Around Failure
&lt;/h1&gt;

&lt;p&gt;This is an underrated idea.&lt;/p&gt;

&lt;p&gt;A strong learning system should make failure useful.&lt;/p&gt;

&lt;p&gt;If a student cannot answer a question, that is data.&lt;/p&gt;

&lt;p&gt;If they repeatedly miss the same concept, that is data.&lt;/p&gt;

&lt;p&gt;If they understand a definition but fail a scenario question, that is data.&lt;/p&gt;

&lt;p&gt;If they perform well immediately but forget the material two weeks later, that is data.&lt;/p&gt;

&lt;p&gt;The system should respond accordingly.&lt;/p&gt;

&lt;p&gt;This is much closer to debugging software than simply reading documentation.&lt;/p&gt;

&lt;p&gt;You do not debug by rereading the entire codebase every time something breaks.&lt;/p&gt;

&lt;p&gt;You identify the failing component.&lt;/p&gt;

&lt;p&gt;Then you investigate it.&lt;/p&gt;

&lt;p&gt;Learning can work similarly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identify the weak concept.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand the failure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice it again.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test it later.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That creates a feedback-driven learning loop.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Good Learning Technology Should Actually Do
&lt;/h1&gt;

&lt;p&gt;The future of educational software should not simply be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Give students more content."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It should be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Help students transform content into learning activities."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That means useful systems should help students move naturally between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assessment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;↓&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feedback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The technology becomes infrastructure around the learning process.&lt;/p&gt;

&lt;p&gt;This is a different product philosophy from simply building another content library.&lt;/p&gt;

&lt;p&gt;The content already exists.&lt;/p&gt;

&lt;p&gt;The challenge is turning it into action.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Student Should Remain in the Loop
&lt;/h1&gt;

&lt;p&gt;There is one final principle that matters.&lt;/p&gt;

&lt;p&gt;Automation should never confuse activity with learning.&lt;/p&gt;

&lt;p&gt;An AI system can generate 100 flashcards.&lt;/p&gt;

&lt;p&gt;That does not mean the student knows the material.&lt;/p&gt;

&lt;p&gt;It can generate a beautiful study guide.&lt;/p&gt;

&lt;p&gt;That does not mean the concepts are understood.&lt;/p&gt;

&lt;p&gt;It can create a difficult practice exam.&lt;/p&gt;

&lt;p&gt;That does not mean the student can solve the problems independently.&lt;/p&gt;

&lt;p&gt;The student still needs to think.&lt;/p&gt;

&lt;p&gt;They need to retrieve.&lt;/p&gt;

&lt;p&gt;They need to make mistakes.&lt;/p&gt;

&lt;p&gt;They need to receive feedback.&lt;/p&gt;

&lt;p&gt;They need to return to difficult material.&lt;/p&gt;

&lt;p&gt;They need to test themselves again.&lt;/p&gt;

&lt;p&gt;The system can make these activities easier to create and organize.&lt;/p&gt;

&lt;p&gt;It cannot remove the need to perform them.&lt;/p&gt;




&lt;h1&gt;
  
  
  From One Lecture to a Learning Engine
&lt;/h1&gt;

&lt;p&gt;The most useful way to think about educational content may be as an input rather than an endpoint.&lt;/p&gt;

&lt;p&gt;A lecture is an input.&lt;/p&gt;

&lt;p&gt;A PDF is an input.&lt;/p&gt;

&lt;p&gt;A textbook chapter is an input.&lt;/p&gt;

&lt;p&gt;The learning system begins when that source is transformed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lecture/PDF&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Understanding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Study Guide&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Questions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Spaced Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Practice Exam&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Feedback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Targeted Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is more than a study workflow.&lt;/p&gt;

&lt;p&gt;It is a feedback system.&lt;/p&gt;

&lt;p&gt;And that may be one of the most important opportunities for AI in education.&lt;/p&gt;

&lt;p&gt;AI does not need to replace the student.&lt;/p&gt;

&lt;p&gt;It can help connect the different activities that students already need to perform.&lt;/p&gt;

&lt;p&gt;The goal is not to make studying require zero effort.&lt;/p&gt;

&lt;p&gt;The goal is to make the effort more deliberate.&lt;/p&gt;

&lt;p&gt;Because the biggest problem students face may not be that they have too little information.&lt;/p&gt;

&lt;p&gt;It may be that the information they already have has nowhere to go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A lecture should not be the end of learning.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It should be the beginning of a system that turns information into knowledge.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Internet Is Changing From Search to Answers. What Do We Lose?</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Mon, 28 Sep 2026 19:47:41 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/the-internet-is-changing-from-search-to-answers-what-do-we-lose-5adb</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/the-internet-is-changing-from-search-to-answers-what-do-we-lose-5adb</guid>
      <description>&lt;p&gt;For years, the internet worked in a relatively simple way.&lt;/p&gt;

&lt;p&gt;You had a question. You opened a search engine, typed a few keywords, looked through a list of results, and clicked the websites that seemed useful. You compared sources, read explanations, followed links, and gradually built your own understanding.&lt;/p&gt;

&lt;p&gt;Today, that experience is changing.&lt;/p&gt;

&lt;p&gt;Instead of giving you a list of websites, AI-powered search experiences can generate summaries, explain concepts, compare options, and answer questions directly. You can ask a follow-up question without opening another page. You can request a summary of a complicated subject and receive one in seconds.&lt;/p&gt;

&lt;p&gt;It feels like progress, and in many ways, it is.&lt;/p&gt;

&lt;p&gt;Finding information is becoming faster and more convenient. But as the internet becomes better at delivering answers, I think we should ask an important question:&lt;/p&gt;

&lt;p&gt;If we no longer need to visit websites to get answers, what happens to the websites that make those answers possible?&lt;/p&gt;

&lt;p&gt;This is not just a debate about search engines or artificial intelligence. It is about how knowledge is created, how independent publishers survive, how developers discover solutions, and how the next generation of the internet will work.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Search Was More Than Finding an Answer
&lt;/h2&gt;

&lt;p&gt;Traditional search engines did not simply provide information. They provided a path to information.&lt;/p&gt;

&lt;p&gt;When you searched for a programming error, you might find an official documentation page, a GitHub issue, a Stack Overflow discussion, a personal blog, or a tutorial written by someone who had encountered the same problem.&lt;/p&gt;

&lt;p&gt;Each source offered a different perspective.&lt;/p&gt;

&lt;p&gt;Official documentation explained the intended behavior. Community discussions revealed confusing edge cases. Personal blogs described practical mistakes. GitHub issues sometimes exposed bugs that were not documented anywhere else.&lt;/p&gt;

&lt;p&gt;You had to navigate these sources, compare explanations, and decide which information applied to your situation.&lt;/p&gt;

&lt;p&gt;That process could be frustrating, but it also exposed you to information you did not initially know you needed.&lt;/p&gt;

&lt;p&gt;An AI-generated answer changes this experience. It can combine information into a single explanation, removing much of the effort required to find and compare sources.&lt;/p&gt;

&lt;p&gt;For straightforward questions, this is genuinely useful.&lt;/p&gt;

&lt;p&gt;But the process of searching, comparing, and reading also served another purpose: it connected readers with the people and communities that created the information.&lt;/p&gt;

&lt;p&gt;When that connection disappears, the consequences extend beyond convenience.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. AI Answers Are Changing How People Visit Websites
&lt;/h2&gt;

&lt;p&gt;This shift is already visible in research on online browsing behavior.&lt;/p&gt;

&lt;p&gt;A Pew Research Center study published in July 2025 analyzed 68,879 Google searches conducted by 900 US adults during March 2025. The researchers found that users who encountered an AI summary clicked a traditional search result in 8% of visits, compared with 15% of visits without an AI summary. Links within AI summaries were clicked in just 1% of visits where summaries appeared.&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%2Fwww.google.com%2Fs2%2Ffavicons%3Fdomain%3Dhttps%3A%2F%2Fwww.pewresearch.org%26sz%3D32" 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%2Fwww.google.com%2Fs2%2Ffavicons%3Fdomain%3Dhttps%3A%2F%2Fwww.pewresearch.org%26sz%3D32" width="32" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pew Research Center&lt;/p&gt;

&lt;p&gt;+1&lt;/p&gt;

&lt;p&gt;Research context: These figures describe the observed browsing behavior of the study participants during the study period. They are not universal click-through rates for every search engine, country, or type of query.&lt;/p&gt;

&lt;p&gt;The findings illustrate an important change in user behavior.&lt;/p&gt;

&lt;p&gt;When an answer appears directly on the results page, some users have less reason to visit another website.&lt;/p&gt;

&lt;p&gt;Imagine searching for a simple technical question, such as how to convert a string to an integer in Python. An AI summary might provide a working example, explain the function, and mention a common error.&lt;/p&gt;

&lt;p&gt;For a quick task, that could be everything you need.&lt;/p&gt;

&lt;p&gt;Now consider a more complicated question: how should you design authentication for a production application?&lt;/p&gt;

&lt;p&gt;A short answer might explain the basic concepts, but you may still need official documentation, implementation examples, security guidance, and discussions of edge cases.&lt;/p&gt;

&lt;p&gt;The challenge is that AI search can present both kinds of answers in a similarly convenient format. Users may receive a concise explanation without immediately knowing what important context is missing.&lt;/p&gt;

&lt;p&gt;The convenience is real. So is the possibility that useful sources receive fewer visits.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. What Happens to Independent Publishers?
&lt;/h2&gt;

&lt;p&gt;Websites require time, effort, and often money to maintain.&lt;/p&gt;

&lt;p&gt;Independent developers write tutorials. Researchers publish explanations. Journalists investigate stories. Educators create learning materials. Small businesses maintain documentation to help customers solve problems.&lt;/p&gt;

&lt;p&gt;Many of these creators depend on some combination of advertising revenue, subscriptions, donations, product sales, consulting, and referrals.&lt;/p&gt;

&lt;p&gt;Search traffic can help readers discover their work.&lt;/p&gt;

&lt;p&gt;If an AI system uses information from across the web to generate an answer, but fewer users visit the original websites, the relationship between information creation and its economic rewards becomes more complicated.&lt;/p&gt;

&lt;p&gt;A creator might invest hours testing a solution, documenting an edge case, and publishing a detailed tutorial. An AI-generated summary could communicate the central idea in a few sentences, satisfying a user's immediate need without generating a visit to the original article.&lt;/p&gt;

&lt;p&gt;This does not mean every AI answer replaces a website visit. Some people will still click through to read detailed explanations, verify sources, or explore related material. Search behavior also varies by topic and intent.&lt;/p&gt;

&lt;p&gt;However, the possibility of reduced referral traffic raises a difficult question.&lt;/p&gt;

&lt;p&gt;If creators receive less traffic from the systems that use their work, how will they continue producing high-quality information?&lt;/p&gt;

&lt;p&gt;This is not only a concern for publishers trying to protect their income. It affects the availability of future knowledge.&lt;/p&gt;

&lt;p&gt;A healthy internet needs people who are willing and able to investigate problems, test solutions, maintain documentation, and share what they learn.&lt;/p&gt;

&lt;p&gt;If those activities become harder to sustain financially, the long-term effects could reach well beyond individual websites.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Risk of Losing Original Experience
&lt;/h2&gt;

&lt;p&gt;There is a difference between knowing an answer and understanding how someone arrived at it.&lt;/p&gt;

&lt;p&gt;Consider a developer who publishes a tutorial about debugging a database connection problem.&lt;/p&gt;

&lt;p&gt;The article might explain the error message, but its real value could be in the details: the operating system, the database version, the failed configuration, the misleading error, and the exact change that fixed the problem.&lt;/p&gt;

&lt;p&gt;These details often come from experience rather than simply knowing the correct command.&lt;/p&gt;

&lt;p&gt;An AI system can summarize the explanation, but a short summary may leave out the conditions under which the solution works. If the reader receives only the summary, they might miss the details needed to apply it safely.&lt;/p&gt;

&lt;p&gt;This becomes particularly important in software development.&lt;/p&gt;

&lt;p&gt;A code snippet that works in a demonstration may fail in production because of concurrency, permissions, input validation, performance constraints, or differences between software versions.&lt;/p&gt;

&lt;p&gt;Original articles often preserve the reasoning behind a solution, including failed attempts and lessons learned.&lt;/p&gt;

&lt;p&gt;For example, a developer writing about a URL shortener might explain why a database constraint was necessary to prevent duplicate codes, how invalid URLs were handled, and what happened when two requests attempted to create the same identifier.&lt;/p&gt;

&lt;p&gt;Those experiences provide context that is difficult to capture in a generic answer.&lt;/p&gt;

&lt;p&gt;AI summaries can help people access information more quickly. But we should not confuse a shorter explanation with a complete understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The Internet Could Become More Convenient but Less Diverse
&lt;/h2&gt;

&lt;p&gt;Another potential loss is diversity of perspective.&lt;/p&gt;

&lt;p&gt;When you visit several websites, you encounter different writing styles, opinions, priorities, and ways of explaining the same problem.&lt;/p&gt;

&lt;p&gt;One developer may prefer a minimal implementation. Another may emphasize security. A third may explain the historical reasons behind a design decision.&lt;/p&gt;

&lt;p&gt;Reading these different perspectives helps you recognize that technical problems rarely have only one reasonable solution.&lt;/p&gt;

&lt;p&gt;AI-generated answers can bring multiple perspectives together, but they can also present a single synthesized explanation that hides the disagreements or uncertainty behind it.&lt;/p&gt;

&lt;p&gt;This creates a subtle risk.&lt;/p&gt;

&lt;p&gt;Readers may become accustomed to receiving one polished answer instead of exploring the range of ideas available across the web.&lt;/p&gt;

&lt;p&gt;That does not mean AI answers are inherently less diverse. Their quality depends on the sources they use, how they synthesize information, and whether they accurately communicate disagreement and uncertainty.&lt;/p&gt;

&lt;p&gt;The concern is what happens when the summarized answer becomes the user's entire information experience.&lt;/p&gt;

&lt;p&gt;A search engine that directs you to several sources invites exploration. An answer engine can make exploration feel unnecessary.&lt;/p&gt;

&lt;p&gt;For routine questions, that may be a reasonable trade-off. For subjects involving security, public policy, scientific uncertainty, or important personal decisions, the ability to examine original evidence remains valuable.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What Developers Could Lose
&lt;/h2&gt;

&lt;p&gt;For developers, the changing relationship between search and websites deserves particular attention.&lt;/p&gt;

&lt;p&gt;The open web has traditionally provided a large, distributed knowledge base. Developers can search error messages, inspect code examples, read official documentation, and learn from discussions about unusual bugs.&lt;/p&gt;

&lt;p&gt;Much of this knowledge exists because people chose to publish their experiences.&lt;/p&gt;

&lt;p&gt;A developer who encounters a strange framework issue might discover a small personal blog describing the exact problem. That blog may have limited traffic, but it can be extremely useful to someone facing the same issue.&lt;/p&gt;

&lt;p&gt;If discovery increasingly happens through generated answers, smaller sources could become harder to find, especially when their content is summarized without a strong reason for users to visit the original page.&lt;br&gt;
&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
There is also a potential feedback problem.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Developers publish solutions and documentation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI systems use publicly accessible information to help answer questions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Users receive answers without necessarily visiting the original sources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some creators receive less traffic or fewer opportunities to earn revenue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fewer creators may have the resources or motivation to maintain detailed public resources.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a possible risk, not an inevitable outcome. AI tools can also direct users to useful documentation, introduce readers to unfamiliar projects, and help more people discover technical knowledge.&lt;/p&gt;

&lt;p&gt;The question is whether those benefits will be sufficient to sustain the communities that produce the information.&lt;/p&gt;

&lt;p&gt;For developers who rely on the open web, preserving access to original documentation, code repositories, issue discussions, and tested examples remains important.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Can AI Search and the Open Web Coexist?
&lt;/h2&gt;

&lt;p&gt;I do not think the solution is to reject AI-powered search.&lt;/p&gt;

&lt;p&gt;There are clear benefits to generating direct answers. People can find information faster, ask follow-up questions naturally, and get explanations adapted to their level of understanding.&lt;/p&gt;

&lt;p&gt;AI can also help users discover relevant concepts they might not have known how to search for.&lt;/p&gt;

&lt;p&gt;The challenge is designing these experiences so that convenience does not come at the expense of the wider information ecosystem.&lt;/p&gt;

&lt;p&gt;There are several practical ways to move in that direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make Sources Visible and Useful
&lt;/h3&gt;

&lt;p&gt;An AI-generated answer should make it easy to identify and visit the sources behind important claims.&lt;/p&gt;

&lt;p&gt;Source links should lead to the relevant material, not merely a generic homepage. Readers should be able to distinguish between information supported by original research, official documentation, and secondary commentary.&lt;/p&gt;

&lt;p&gt;Citations are most valuable when they help users investigate a claim rather than simply decorate an answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give Original Work a Reason to Be Visited
&lt;/h3&gt;

&lt;p&gt;Websites can provide value that is difficult to reproduce in a short summary.&lt;/p&gt;

&lt;p&gt;For technical publishers, that could mean runnable examples, downloadable projects, interactive demonstrations, benchmark results, detailed experiments, and explanations of failure cases.&lt;/p&gt;

&lt;p&gt;For researchers and journalists, it might mean access to underlying data, methodology, interviews, original documents, and detailed reporting.&lt;/p&gt;

&lt;p&gt;The objective should not be to make information unnecessarily difficult to access. It should be to make the original resource useful beyond the basic answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure More Than Convenience
&lt;/h3&gt;

&lt;p&gt;Search systems are often evaluated by how effectively they satisfy a user's information need. That is important, but the broader ecosystem matters too.&lt;/p&gt;

&lt;p&gt;It is also worth asking whether the system directs users toward reliable sources, preserves attribution, supports independent publishers, and encourages further investigation when a question requires more depth.&lt;/p&gt;

&lt;p&gt;These are different goals, and they may sometimes conflict. A short answer can satisfy a user's immediate need while reducing the likelihood of a click.&lt;/p&gt;

&lt;p&gt;Recognizing that trade-off is an important part of designing responsible information systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. What Can Bloggers and Developers Do About It?
&lt;/h2&gt;

&lt;p&gt;The shift toward AI-generated answers creates challenges, but it also gives creators a reason to rethink how they publish information.&lt;/p&gt;

&lt;p&gt;If your work simply repeats information that already appears on hundreds of websites, it may be difficult to distinguish your article from a generated summary.&lt;/p&gt;

&lt;p&gt;Originality becomes more important.&lt;/p&gt;

&lt;p&gt;For bloggers and developers, a few strategies are worth considering.&lt;/p&gt;

&lt;p&gt;Publish firsthand experience. Share what you built, what failed, what you measured, and what you learned. Real experiments provide context that generic explanations often lack.&lt;/p&gt;

&lt;p&gt;Show your evidence. Include source links, code, screenshots, test results, and relevant examples. Make it possible for readers to verify your claims.&lt;/p&gt;

&lt;p&gt;Explain the reasoning. Do not just provide the final solution. Explain why it works, when it might fail, and what alternatives you considered.&lt;/p&gt;

&lt;p&gt;Build direct relationships with readers. Newsletters, communities, open-source projects, and professional networks can help people discover your work without depending entirely on search traffic.&lt;/p&gt;

&lt;p&gt;Make content genuinely useful. Clear documentation, practical tutorials, original research, and detailed troubleshooting guides serve readers who need more than a quick answer.&lt;/p&gt;

&lt;p&gt;None of these strategies guarantees traffic. Search engines, recommendation systems, and audience preferences will continue to change. But publishing work with distinctive value gives readers a reason to seek it out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Answers Are Useful, but Discovery Still Matters
&lt;/h2&gt;

&lt;p&gt;The internet is moving toward a model in which people can ask questions and receive immediate answers without navigating multiple websites.&lt;/p&gt;

&lt;p&gt;That change offers genuine benefits. It can reduce friction, make complicated subjects easier to approach, and help people find information more efficiently.&lt;/p&gt;

&lt;p&gt;But the internet is not simply a database of facts waiting to be summarized.&lt;/p&gt;

&lt;p&gt;It is also a collection of people, communities, experiments, arguments, documentation, and original discoveries. Websites are not just containers for answers. They are places where knowledge is created, tested, challenged, and improved.&lt;/p&gt;

&lt;p&gt;If AI systems make information easier to consume while making original sources harder to sustain, we could end up with a more convenient internet that has fewer incentives to produce the detailed work on which its answers depend.&lt;/p&gt;

&lt;p&gt;That outcome is not inevitable. Search systems can link to sources, publishers can develop more distinctive resources, and readers can continue exploring beyond the first answer.&lt;/p&gt;

&lt;p&gt;As developers, we should care about this because the open web has always been one of our most valuable learning resources.&lt;/p&gt;

&lt;p&gt;The next time an AI system answers a technical question, consider opening the documentation or article behind it. Read the original explanation. Explore the alternatives. Support the people who took the time to investigate the problem.&lt;/p&gt;

&lt;p&gt;The future of the internet should not be measured only by how quickly we receive an answer, but also by whether the people creating useful knowledge can continue doing so.&lt;/p&gt;

&lt;p&gt;Because when we lose the habit of discovering sources, we risk losing more than clicks. We risk weakening the ecosystem that makes reliable answers possible in the first place.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built a URL Shortener in Python. Here's What Broke</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Thu, 24 Sep 2026 18:54:51 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/i-built-a-url-shortener-in-python-heres-what-broke-5055</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/i-built-a-url-shortener-in-python-heres-what-broke-5055</guid>
      <description>&lt;p&gt;Building a URL shortener sounds almost too simple.&lt;/p&gt;

&lt;p&gt;Take a long URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/articles/how-to-build-a-python-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turn it into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5000/aB91x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When someone visits &lt;code&gt;/aB91x&lt;/code&gt;, redirect them to the original URL.&lt;/p&gt;

&lt;p&gt;That is the basic idea.&lt;/p&gt;

&lt;p&gt;But while building a simple URL shortener with Python, Flask, and SQLite, I discovered that the redirect itself was probably the easiest part of the project.&lt;/p&gt;

&lt;p&gt;The interesting problems appeared around it.&lt;/p&gt;

&lt;p&gt;What happens if two URLs get the same short code?&lt;/p&gt;

&lt;p&gt;What happens if someone submits an invalid URL?&lt;/p&gt;

&lt;p&gt;What happens if the database contains thousands of links?&lt;/p&gt;

&lt;p&gt;What happens if someone tries to use the service for malicious redirects?&lt;/p&gt;

&lt;p&gt;And what happens when a project that worked perfectly on my laptop meets real users?&lt;/p&gt;

&lt;p&gt;This is what I learned while building it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Wanted to Build
&lt;/h2&gt;

&lt;p&gt;I wanted a small application with four basic features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accept a long URL&lt;/li&gt;
&lt;li&gt;Generate a short code&lt;/li&gt;
&lt;li&gt;Store the URL in a database&lt;/li&gt;
&lt;li&gt;Redirect users when they visit the short URL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The architecture looked 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;User
  |
  v
Flask Application
  |
  +---- Generate Short Code
  |
  +---- Store URL
  |
  v
SQLite Database
  |
  v
Short URL
  |
  v
Redirect to Original URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I deliberately kept the first version simple.&lt;/p&gt;

&lt;p&gt;The goal was not to build the next Bitly.&lt;/p&gt;

&lt;p&gt;The goal was to understand what actually happens inside a URL shortener.&lt;/p&gt;




&lt;h1&gt;
  
  
  The First Version
&lt;/h1&gt;

&lt;p&gt;I started with Flask because it provides everything needed for a small HTTP application without adding unnecessary complexity.&lt;/p&gt;

&lt;p&gt;The project structure looked 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;url-shortener/
│
├── app.py
├── database.db
└── templates/
    └── index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I installed Flask:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then created the application.&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;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;database.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;row_factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Row&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;characters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ascii_letters&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&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;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&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;GET&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;POST&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;index&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_code&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO urls (code, url) VALUES (?, ?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;short_url&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;http://localhost:5000/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;code&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;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&amp;lt;code&amp;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;shorten_redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_db&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT url FROM urls WHERE code = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;URL not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__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;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&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;It worked.&lt;/p&gt;

&lt;p&gt;I entered a URL.&lt;/p&gt;

&lt;p&gt;The application generated a short code.&lt;/p&gt;

&lt;p&gt;I clicked the short URL.&lt;/p&gt;

&lt;p&gt;The browser redirected me to the original website.&lt;/p&gt;

&lt;p&gt;For about five minutes, everything looked perfect.&lt;/p&gt;

&lt;p&gt;Then I started asking what could go wrong.&lt;/p&gt;




&lt;h1&gt;
  
  
  Problem 1: Short Codes Can Collide
&lt;/h1&gt;

&lt;p&gt;My first implementation generated a random six-character code.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a91Bc2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But random does not mean unique.&lt;/p&gt;

&lt;p&gt;Eventually, the application could generate the same code twice.&lt;/p&gt;

&lt;p&gt;Imagine the database contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abc123 -&amp;gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then another user creates a URL and the application generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abc123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now what?&lt;/p&gt;

&lt;p&gt;The application cannot safely use the same code for two different URLs.&lt;/p&gt;

&lt;p&gt;The first solution is to check whether the code already exists.&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;def&lt;/span&gt; &lt;span class="nf"&gt;generate_unique_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_code&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT id FROM urls WHERE code = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;existing&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;code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_unique_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much better.&lt;/p&gt;

&lt;p&gt;But there is still another problem.&lt;/p&gt;

&lt;p&gt;Two requests could theoretically check the database at almost the same time and both discover that the code is available.&lt;/p&gt;

&lt;p&gt;That is why the database should also enforce uniqueness.&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application checks.&lt;/p&gt;

&lt;p&gt;The database enforces.&lt;/p&gt;

&lt;p&gt;I learned an important lesson here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Application logic should not be the only thing protecting data integrity.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Problem 2: I Forgot URL Validation
&lt;/h1&gt;

&lt;p&gt;My first version basically trusted whatever the user submitted.&lt;/p&gt;

&lt;p&gt;That means someone could enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or they could enter a completely different URL scheme.&lt;/p&gt;

&lt;p&gt;A basic validation function is better:&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;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_valid_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scheme&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&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;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;netloc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;is_valid_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a complete security system, but it prevents many obviously invalid inputs.&lt;/p&gt;

&lt;p&gt;It also makes the application behave more predictably.&lt;/p&gt;




&lt;h1&gt;
  
  
  Problem 3: I Started Thinking About Security
&lt;/h1&gt;

&lt;p&gt;A URL shortener looks harmless.&lt;/p&gt;

&lt;p&gt;But URL shorteners can be abused.&lt;/p&gt;

&lt;p&gt;Someone could create a short link pointing toward a phishing page, malware download, or other malicious website.&lt;/p&gt;

&lt;p&gt;That means a production URL shortener needs to think about abuse.&lt;/p&gt;

&lt;p&gt;Depending on the application, additional protections could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limiting&lt;/li&gt;
&lt;li&gt;Abuse reporting&lt;/li&gt;
&lt;li&gt;Link scanning&lt;/li&gt;
&lt;li&gt;Domain reputation checks&lt;/li&gt;
&lt;li&gt;CAPTCHA for suspicious activity&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Expiration dates&lt;/li&gt;
&lt;li&gt;Link ownership&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Blocking known malicious destinations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a small learning project, I did not need to implement all of these.&lt;/p&gt;

&lt;p&gt;But realizing that a technically functional application could still be abused changed how I thought about "finished."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Working is not the same thing as production-ready.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Problem 4: The Database Was Too Basic
&lt;/h1&gt;

&lt;p&gt;Initially, my table only contained two useful fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code
url
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was enough for a demo.&lt;/p&gt;

&lt;p&gt;But then I wanted to answer basic questions.&lt;/p&gt;

&lt;p&gt;When was the link created?&lt;/p&gt;

&lt;p&gt;How many times was it visited?&lt;/p&gt;

&lt;p&gt;Who created it?&lt;/p&gt;

&lt;p&gt;Should it expire?&lt;/p&gt;

&lt;p&gt;The schema could evolve into something like:&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;clicks&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expires_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the application can support additional features later.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Short URL
   |
   +-- Original URL
   +-- Created date
   +-- Number of clicks
   +-- Expiration date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This made me realize something important about database design.&lt;/p&gt;

&lt;p&gt;You do not need to design everything perfectly before writing your first line of code.&lt;/p&gt;

&lt;p&gt;But you should think about what information the application will probably need later.&lt;/p&gt;




&lt;h1&gt;
  
  
  Problem 5: Counting Clicks Was Not as Simple as I Expected
&lt;/h1&gt;

&lt;p&gt;I wanted to count how many times each link was opened.&lt;/p&gt;

&lt;p&gt;The obvious solution was:&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UPDATE urls SET clicks = clicks + 1 WHERE code = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&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;Then redirect the user.&lt;/p&gt;

&lt;p&gt;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="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&amp;lt;code&amp;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;shorten_redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_db&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT url FROM urls WHERE code = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;URL not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UPDATE urls SET clicks = clicks + 1 WHERE code = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&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;For a small project, this is fine.&lt;/p&gt;

&lt;p&gt;But with a large number of requests, constantly updating the database for every redirect can become a performance consideration.&lt;/p&gt;

&lt;p&gt;A production system might need a different architecture involving caching, queues, analytics systems, or other infrastructure.&lt;/p&gt;

&lt;p&gt;Again, the simple version worked.&lt;/p&gt;

&lt;p&gt;But scaling changes the problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  Problem 6: SQLite Was Great Until I Thought About Scale
&lt;/h1&gt;

&lt;p&gt;SQLite was perfect for learning.&lt;/p&gt;

&lt;p&gt;There was no database server to configure.&lt;/p&gt;

&lt;p&gt;The entire database lived in one file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That made development extremely easy.&lt;/p&gt;

&lt;p&gt;But a public URL-shortening service could eventually need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More concurrent writes&lt;/li&gt;
&lt;li&gt;Replication&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;High availability&lt;/li&gt;
&lt;li&gt;Better scaling characteristics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, a production application might use a database such as PostgreSQL or another system designed around the application's workload.&lt;/p&gt;

&lt;p&gt;This taught me another useful lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The best technology for a prototype is not always the best technology for production.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That does not mean SQLite was a bad choice.&lt;/p&gt;

&lt;p&gt;It was the right level of complexity for what I was building.&lt;/p&gt;




&lt;h1&gt;
  
  
  Problem 7: My URL Codes Were Not Really "Short"
&lt;/h1&gt;

&lt;p&gt;I initially thought that six characters was automatically the correct answer.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;The length of the code depends on the number of available characters and the number of URLs you need to represent.&lt;/p&gt;

&lt;p&gt;If the code uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a-z
A-Z
0-9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;there are 62 possible characters.&lt;/p&gt;

&lt;p&gt;With six characters, there are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;62^6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;possible combinations.&lt;/p&gt;

&lt;p&gt;That gives a large address space for a small project.&lt;/p&gt;

&lt;p&gt;But if the application becomes extremely large, code length and collision probability become important design considerations.&lt;/p&gt;

&lt;p&gt;This was one of those moments where a simple programming project suddenly became a small lesson in probability and system design.&lt;/p&gt;




&lt;h1&gt;
  
  
  Problem 8: My Error Handling Was Terrible
&lt;/h1&gt;

&lt;p&gt;The first version assumed everything would work.&lt;/p&gt;

&lt;p&gt;Real applications do not get that luxury.&lt;/p&gt;

&lt;p&gt;What if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The database is unavailable?&lt;/li&gt;
&lt;li&gt;The submitted URL is empty?&lt;/li&gt;
&lt;li&gt;The generated code already exists?&lt;/li&gt;
&lt;li&gt;The user requests a nonexistent code?&lt;/li&gt;
&lt;li&gt;The database insert fails?&lt;/li&gt;
&lt;li&gt;The application receives malformed input?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of letting unexpected errors reach the user, the application should handle expected failures.&lt;/p&gt;

&lt;p&gt;For example:&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO urls (code, url) VALUES (?, ?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntegrityError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Could not create short URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;

&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Error handling is not the most exciting part of programming.&lt;/p&gt;

&lt;p&gt;But it is one of the things that separates a demo from a reliable application.&lt;/p&gt;




&lt;h1&gt;
  
  
  What the Final Flow Looked Like
&lt;/h1&gt;

&lt;p&gt;After fixing the main problems, the application flow became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User submits URL
       |
       v
Validate URL
       |
       v
Generate short code
       |
       v
Check uniqueness
       |
       v
Store in database
       |
       v
Return short URL
       |
       v
User opens short URL
       |
       v
Find code in database
       |
       v
Record click
       |
       v
Redirect to destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was still a small application.&lt;/p&gt;

&lt;p&gt;But it was now a much better representation of how a real system needs to behave.&lt;/p&gt;




&lt;h1&gt;
  
  
  What I Would Add Next
&lt;/h1&gt;

&lt;p&gt;If I continued developing the project, I would add:&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;Users could manage the links they created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expiration
&lt;/h3&gt;

&lt;p&gt;Links could automatically stop working after a specific date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analytics
&lt;/h3&gt;

&lt;p&gt;Users could see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total clicks
Clicks per day
Referrer
Device type
Country
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analytics would need to be designed carefully because collecting additional information also introduces privacy considerations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom aliases
&lt;/h3&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aB91x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;users could choose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rate limiting
&lt;/h3&gt;

&lt;p&gt;This would help prevent automated abuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;Frequently accessed URLs could potentially be served without querying the primary database every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Production database
&lt;/h3&gt;

&lt;p&gt;If the application grew significantly, I would consider moving beyond SQLite depending on the workload and deployment architecture.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Biggest Lesson
&lt;/h1&gt;

&lt;p&gt;The biggest lesson was not how to generate a six-character string.&lt;/p&gt;

&lt;p&gt;It was learning how quickly a simple idea becomes a system.&lt;/p&gt;

&lt;p&gt;At first, the project looked 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;Long URL
   ↓
Short Code
   ↓
Redirect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After thinking about real usage, it became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input validation
       ↓
Code generation
       ↓
Uniqueness
       ↓
Database constraints
       ↓
Security
       ↓
Analytics
       ↓
Error handling
       ↓
Performance
       ↓
Abuse prevention
       ↓
Monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is what I like about small projects.&lt;/p&gt;

&lt;p&gt;You can start with something that looks almost trivial and slowly discover the engineering decisions hiding underneath.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Building a URL shortener is one of those projects that looks like a beginner exercise until you start asking production-level questions.&lt;/p&gt;

&lt;p&gt;The redirect itself took very little code.&lt;/p&gt;

&lt;p&gt;The difficult part was everything around it.&lt;/p&gt;

&lt;p&gt;I learned about database constraints, URL validation, collision handling, security, analytics, error handling, and scalability from one relatively small project.&lt;/p&gt;

&lt;p&gt;That is why I think developers should build small applications instead of only following tutorials.&lt;/p&gt;

&lt;p&gt;A tutorial can show you the happy path.&lt;/p&gt;

&lt;p&gt;A real project shows you what breaks.&lt;/p&gt;

&lt;p&gt;And those broken parts are often where the actual learning happens.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Design Tool APIs for AI Agents</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Tue, 22 Sep 2026 20:24:44 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-design-tool-apis-for-ai-agents-20pa</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/how-to-design-tool-apis-for-ai-agents-20pa</guid>
      <description>&lt;p&gt;The architecture, schemas, error handling, and safety patterns behind reliable agent tool use&lt;/p&gt;

&lt;p&gt;AI agents become useful when they can do more than generate text.&lt;/p&gt;

&lt;p&gt;They need to search databases, read documents, call APIs, create records, execute code, send messages, update systems, and sometimes recover from failures.&lt;/p&gt;

&lt;p&gt;That means an agent is only as capable as the tools it can use.&lt;/p&gt;

&lt;p&gt;But there is an important engineering distinction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A tool API designed for humans is not necessarily a good tool API for an AI agent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Traditional APIs are usually designed around predictable software clients. The client developer knows the API contract, understands the parameter names, validates inputs, handles errors, and writes the control flow.&lt;/p&gt;

&lt;p&gt;An AI agent is different.&lt;/p&gt;

&lt;p&gt;The model has to decide &lt;strong&gt;whether to call a tool, which tool to call, what arguments to provide, and how to interpret the result&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That makes tool design part of the agent's reasoning architecture.&lt;/p&gt;

&lt;p&gt;Modern agent frameworks expose tools through structured schemas. For example, MCP tools define names, descriptions, input schemas, and optionally output schemas. MCP implementations can also expose behavioral hints such as read-only or destructive behavior. ([Model Context Protocol][1])&lt;/p&gt;

&lt;p&gt;So the question is no longer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How do I expose my API to an AI?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"How do I design an API that an AI can reliably reason about?"&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. A Tool Is an Interface Between Reasoning and Software
&lt;/h2&gt;

&lt;p&gt;Consider a simple agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  ↓
AI Agent
  ↓
Model decides what to do
  ↓
Tool
  ↓
Application / Database / API
  ↓
Tool result
  ↓
Model continues reasoning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool sits directly between the model and your software.&lt;/p&gt;

&lt;p&gt;Suppose a user says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find my latest invoice and tell me whether it has been paid."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent may need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the user.&lt;/li&gt;
&lt;li&gt;Search invoices.&lt;/li&gt;
&lt;li&gt;Select the relevant invoice.&lt;/li&gt;
&lt;li&gt;Inspect its payment status.&lt;/li&gt;
&lt;li&gt;Explain the result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You could expose one enormous function:&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;get_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;invoice_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;customer_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;include_payment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;include_items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;include_customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, this might work.&lt;/p&gt;

&lt;p&gt;For an AI agent, it creates a much harder decision problem.&lt;/p&gt;

&lt;p&gt;A better tool surface might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_invoices
get_invoice
get_invoice_payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tool has a narrower responsibility.&lt;/p&gt;

&lt;p&gt;This leads to the first principle:&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Tools Around Decisions, Not Database Operations
&lt;/h2&gt;

&lt;p&gt;A tool should represent a meaningful capability the agent can reason about.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execute_sql
call_api
update_database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_invoices
create_invoice
cancel_invoice
get_payment_status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second set gives the model a semantic vocabulary.&lt;/p&gt;

&lt;p&gt;The model does not need to understand your internal database structure.&lt;/p&gt;

&lt;p&gt;It only needs to understand:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"When should I use this capability?"&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  2. Tool Names Are Part of the Interface
&lt;/h1&gt;

&lt;p&gt;Developers sometimes treat tool names as implementation details.&lt;/p&gt;

&lt;p&gt;For agents, they are part of the model-facing API.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get_data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_customer_orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second immediately communicates intent.&lt;br&gt;
&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
A good tool name should answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What capability does this tool provide?&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_documents
get_document
create_presentation
generate_chart
send_email
schedule_meeting
get_weather
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avoid names that require internal knowledge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;process_v2
execute_operation
handler_7
data_service
run_query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent should not need to inspect your source code to understand the purpose of a tool.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. Tool Descriptions Are Instructions for the Model
&lt;/h1&gt;

&lt;p&gt;This is one of the most overlooked parts of agent engineering.&lt;/p&gt;

&lt;p&gt;A tool description is not just API documentation.&lt;/p&gt;

&lt;p&gt;It becomes part of the model's decision context.&lt;/p&gt;

&lt;p&gt;Google's Agent Development Kit documentation, for example, notes that a tool's Python docstring becomes part of what the model sees and recommends writing it clearly because it tells the model when and how to use the tool. ([Google GitHub][2])&lt;/p&gt;

&lt;p&gt;Consider:&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;def&lt;/span&gt; &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Search documents.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is technically valid.&lt;/p&gt;

&lt;p&gt;But it leaves important questions unanswered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What should &lt;code&gt;query&lt;/code&gt; contain?&lt;/li&gt;
&lt;li&gt;Is this semantic search?&lt;/li&gt;
&lt;li&gt;Should the agent use it for exact matches?&lt;/li&gt;
&lt;li&gt;What does it return?&lt;/li&gt;
&lt;li&gt;When should the agent prefer another tool?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better description:&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;def&lt;/span&gt; &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Search the document library using semantic and keyword matching.

    Use this when the user asks about information that may exist
    inside uploaded documents.

    Do not use this tool for exact document IDs.

    Returns matching documents with their titles, IDs,
    relevance scores, and short excerpts.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the model has decision guidance.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. Treat the Tool Schema as a Contract
&lt;/h1&gt;

&lt;p&gt;A tool should have a strict input contract.&lt;/p&gt;

&lt;p&gt;For 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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"search_documents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Search the document library for relevant content."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputSchema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The information or concept to search for."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is significantly better than:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because the schema communicates constraints.&lt;/p&gt;

&lt;p&gt;Modern MCP tooling uses JSON Schema for tool inputs and can also define structured output schemas. Current MCP SDK documentation shows schemas being used both to describe what arguments a tool accepts and to validate those arguments before the handler executes. ([MCP TypeScript SDK][3])&lt;/p&gt;

&lt;p&gt;That gives you an important architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model
  ↓
Tool schema
  ↓
Validation
  ↓
Tool implementation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not rely on the model alone for validation.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Keep Parameters Small and Semantic
&lt;/h1&gt;

&lt;p&gt;One common mistake is exposing every possible API parameter to the model.&lt;/p&gt;

&lt;p&gt;Imagine:&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;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"organization_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"locale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include_deleted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include_archived"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include_metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include_permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sort_direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"page_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cursor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;This may be appropriate for a low-level backend API.&lt;/p&gt;

&lt;p&gt;It is usually a poor model-facing tool.&lt;/p&gt;

&lt;p&gt;The model has to reason about too many choices.&lt;/p&gt;

&lt;p&gt;Instead, create an agent-oriented interface:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer invoices from March"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend can translate that into the complex internal API call.&lt;/p&gt;

&lt;p&gt;This creates a useful separation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Agent-facing API
                        ↓
                Simple tool contract
                        ↓
                 Adapter layer
                        ↓
                  Internal APIs
                        ↓
                    Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your internal architecture can remain complicated.&lt;/p&gt;

&lt;p&gt;The model-facing interface should remain understandable.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. Avoid Ambiguous Parameters
&lt;/h1&gt;

&lt;p&gt;Names matter.&lt;/p&gt;

&lt;p&gt;Compare:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&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;with:&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;"invoice_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&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 second is safer because the semantic meaning is explicit.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is ambiguous.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;start_date
end_date
created_after
created_before
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document_type
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI systems operate through semantic interpretation.&lt;/p&gt;

&lt;p&gt;Reducing ambiguity reduces unnecessary reasoning.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Make Invalid States Difficult to Express
&lt;/h1&gt;

&lt;p&gt;Suppose your API expects a date range.&lt;/p&gt;

&lt;p&gt;Instead of allowing:&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;"start_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tomorrow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"end_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yesterday"&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;and hoping the backend handles it, define explicit validation.&lt;/p&gt;

&lt;p&gt;For 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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"end_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"start_date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"end_date"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then validate the relationship server-side:&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;start_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;end_date&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;InvalidDateRange&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schema validation catches structural problems.&lt;/p&gt;

&lt;p&gt;Business validation catches semantic problems.&lt;/p&gt;

&lt;p&gt;You need both.&lt;/p&gt;




&lt;h1&gt;
  
  
  8. Design Tool Outputs for the Next Decision
&lt;/h1&gt;

&lt;p&gt;Tool design is not only about inputs.&lt;/p&gt;

&lt;p&gt;The output is equally important.&lt;/p&gt;

&lt;p&gt;Suppose a search tool returns:&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;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model has to figure out what each field means.&lt;/p&gt;

&lt;p&gt;Instead:&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;"results"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"document_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Product Strategy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"relevance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.91&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"excerpt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The product strategy focuses on..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_results"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&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;Now the model has useful information for the next step.&lt;/p&gt;

&lt;p&gt;The output should help answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What should the agent do next?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is especially important for multi-step agents.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Use Structured Outputs When the Next Step Needs Structure
&lt;/h1&gt;

&lt;p&gt;Suppose a tool returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The customer has three unpaid invoices. The oldest
was created on January 12 and is currently overdue.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A human can understand it.&lt;/p&gt;

&lt;p&gt;Another model call has to interpret the text.&lt;/p&gt;

&lt;p&gt;A structured result is easier to consume:&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;"customer_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cus_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"unpaid_invoices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"oldest_invoice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"invoice_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inv_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"overdue"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP currently supports optional &lt;code&gt;outputSchema&lt;/code&gt; definitions for structured tool results, and its SDK can validate structured content against that schema. ([MCP TypeScript SDK][3])&lt;/p&gt;

&lt;p&gt;Structured output becomes particularly valuable when tools are chained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search customer
      ↓
Get invoice
      ↓
Check payment
      ↓
Generate response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step should produce information that the next step can reliably consume.&lt;/p&gt;




&lt;h1&gt;
  
  
  10. Do Not Hide Side Effects
&lt;/h1&gt;

&lt;p&gt;There is a major difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cancel_order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first reads information.&lt;/p&gt;

&lt;p&gt;The second changes state.&lt;/p&gt;

&lt;p&gt;Your tool interface should make that distinction obvious.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get_customer
search_orders
get_invoice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create_customer
update_customer
cancel_order
send_email
delete_document
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MCP tool metadata can include behavioral hints such as &lt;code&gt;readOnlyHint&lt;/code&gt;, &lt;code&gt;destructiveHint&lt;/code&gt;, and &lt;code&gt;idempotentHint&lt;/code&gt;. These are useful signals, although they should not be treated as security controls. ([Model Context Protocol][1])&lt;/p&gt;

&lt;p&gt;A practical architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read operation
    ↓
Can often execute automatically

Write operation
    ↓
Validate
    ↓
Check permissions
    ↓
Potential approval
    ↓
Execute

Destructive operation
    ↓
Validate
    ↓
Explicit confirmation
    ↓
Execute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tool itself should enforce authorization.&lt;/p&gt;

&lt;p&gt;Never assume that because the model selected a tool, the action is authorized.&lt;/p&gt;




&lt;h1&gt;
  
  
  11. Idempotency Matters More With Agents
&lt;/h1&gt;

&lt;p&gt;Agents retry.&lt;/p&gt;

&lt;p&gt;Networks fail.&lt;/p&gt;

&lt;p&gt;Models repeat actions.&lt;/p&gt;

&lt;p&gt;A user may say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Send the report to Sarah."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;send_report()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server processes it.&lt;/p&gt;

&lt;p&gt;The response is lost.&lt;/p&gt;

&lt;p&gt;The agent does not know whether the operation succeeded.&lt;/p&gt;

&lt;p&gt;It might call the tool again.&lt;/p&gt;

&lt;p&gt;Now Sarah receives two reports.&lt;/p&gt;

&lt;p&gt;For side-effecting tools, consider idempotency keys:&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;"recipient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sarah@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"report_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"report_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"idempotency_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agent-run-789-send-report"&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 backend can recognize that the operation has already been performed.&lt;/p&gt;

&lt;p&gt;This turns retries from a dangerous behavior into a manageable one.&lt;/p&gt;




&lt;h1&gt;
  
  
  12. Design Errors for Recovery
&lt;/h1&gt;

&lt;p&gt;Traditional APIs often return:&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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bad Request"&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;That is not very useful to an agent.&lt;/p&gt;

&lt;p&gt;Consider:&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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INSUFFICIENT_PERMISSION"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The current user cannot access this project."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"retryable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ask the user to select a project they have access to."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the model has information for the next decision.&lt;/p&gt;

&lt;p&gt;Useful error categories include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INVALID_ARGUMENT
NOT_FOUND
PERMISSION_DENIED
RATE_LIMITED
TEMPORARY_FAILURE
CONFLICT
REQUIRES_CONFIRMATION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model should be able to distinguish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Try again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Change the request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ask the user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  13. Separate Retryable and Non-Retryable Errors
&lt;/h1&gt;

&lt;p&gt;This distinction becomes critical in autonomous systems.&lt;/p&gt;

&lt;p&gt;For 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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RATE_LIMITED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retryable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retry_after_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;versus:&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INVALID_CUSTOMER_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retryable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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 agent can then make a more informed decision.&lt;/p&gt;

&lt;p&gt;A simple retry policy might look 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;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retryable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;retry_with_backoff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INVALID_ARGUMENT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;reconsider_arguments&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PERMISSION_DENIED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;ask_user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;stop_and_report&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much safer than blindly retrying every failure.&lt;/p&gt;




&lt;h1&gt;
  
  
  14. Keep Tools Focused
&lt;/h1&gt;

&lt;p&gt;A tool should generally have one clear responsibility.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;manage_customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create
read
update
delete
search
merge
archive
restore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the model a large decision surface.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_customers
get_customer
create_customer
update_customer
archive_customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tool has a more precise meaning.&lt;/p&gt;

&lt;p&gt;Google's MCP security guidance similarly recommends keeping MCP tools focused on a single responsibility. ([Google GitHub][4])&lt;/p&gt;

&lt;p&gt;The principle is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fewer decisions per tool usually means clearer decisions for the agent.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  15. But Do Not Create Hundreds of Tiny Tools
&lt;/h1&gt;

&lt;p&gt;There is another failure mode.&lt;/p&gt;

&lt;p&gt;You can overcorrect.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get_user_name
get_user_email
get_user_timezone
get_user_language
get_user_company
get_user_role
get_user_status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the model has to discover seven tools just to understand one user.&lt;/p&gt;

&lt;p&gt;Tool granularity should match meaningful agent actions.&lt;/p&gt;

&lt;p&gt;A useful test is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Would an agent naturally think of this as a distinct capability?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If yes, it may deserve its own tool.&lt;/p&gt;

&lt;p&gt;If not, it may belong inside another operation.&lt;/p&gt;




&lt;h1&gt;
  
  
  16. Tool Descriptions Should Explain When Not to Use Them
&lt;/h1&gt;

&lt;p&gt;This is an advanced but powerful pattern.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search documents.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search the document library for information contained
in uploaded documents.

Use this when the answer may exist inside user documents.

Do not use this for general web research.
Do not use this when the user provides an exact document ID.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The negative guidance reduces tool confusion.&lt;/p&gt;

&lt;p&gt;This becomes increasingly important when an agent has many tools with overlapping capabilities.&lt;/p&gt;




&lt;h1&gt;
  
  
  17. Build an Agent-Friendly API Layer
&lt;/h1&gt;

&lt;p&gt;Your existing backend probably looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend
    ↓
REST API
    ↓
Services
    ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding agents does not mean the model should receive unrestricted access to that REST API.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌──────────────┐
                    │    Agent     │
                    └──────┬───────┘
                           ↓
                  ┌─────────────────┐
                  │  Tool Layer     │
                  │                 │
                  │ search_docs     │
                  │ create_report   │
                  │ send_report     │
                  └────────┬────────┘
                           ↓
                  ┌─────────────────┐
                  │ Adapter Layer   │
                  └────────┬────────┘
                           ↓
                  ┌─────────────────┐
                  │ Internal APIs   │
                  └────────┬────────┘
                           ↓
                     Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adapter layer is valuable because your internal APIs can evolve independently from the agent interface.&lt;/p&gt;




&lt;h1&gt;
  
  
  18. Example: Building a Document Tool
&lt;/h1&gt;

&lt;p&gt;Let's build a simple Python tool.&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;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypedDict&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DocumentResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;document_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;relevance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DocumentResult&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Search uploaded documents for information relevant to the query.

    Use this when the user asks about information that may exist
    inside uploaded documents.

    Do not use this for general web research.

    Args:
        query: Natural-language description of the information to find.
        limit: Maximum number of results. Must be between 1 and 10.

    Returns:
        Matching documents with titles, excerpts, and relevance scores.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query cannot be empty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;10&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit must be between 1 and 10&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document_search_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;excerpt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;relevance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what this tool does not expose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database connection
SQL query
embedding model
vector database
chunk size
index name
internal storage path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are implementation details.&lt;/p&gt;

&lt;p&gt;The agent needs a capability, not your infrastructure.&lt;/p&gt;




&lt;h1&gt;
  
  
  19. Tool APIs Need Authentication Too
&lt;/h1&gt;

&lt;p&gt;A common mistake is to think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The model is inside our application, so the tool is trusted."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model is not an authorization system.&lt;/p&gt;

&lt;p&gt;Every tool invocation should still pass through normal security controls.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent
 ↓
Tool
 ↓
Authenticated identity
 ↓
Authorization
 ↓
Validation
 ↓
Business logic
 ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never let a model-generated argument bypass authorization.&lt;/p&gt;

&lt;p&gt;Bad:&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;def&lt;/span&gt; &lt;span class="nf"&gt;get_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&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;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better:&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;def&lt;/span&gt; &lt;span class="nf"&gt;get_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model chooses &lt;strong&gt;what it wants to do&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Your application decides &lt;strong&gt;whether it is allowed to do it&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  20. Add Observability to Every Tool Call
&lt;/h1&gt;

&lt;p&gt;When an agent makes a wrong decision, you need to know why.&lt;/p&gt;

&lt;p&gt;Log at least:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agent_run_id
tool_name
tool_version
arguments
user_id
timestamp
latency
result_status
error_code
retry_count
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For sensitive systems, carefully control what arguments and results are logged.&lt;/p&gt;

&lt;p&gt;A useful trace might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run: agent_9821

09:41:02
Tool: search_documents
Arguments:
query = "Q3 pricing strategy"

09:41:03
Result:
3 documents

09:41:04
Tool: get_document
Arguments:
document_id = "doc_918"

09:41:04
Result:
success

09:41:07
Tool: create_summary
Arguments:
document_id = "doc_918"

09:41:09
Result:
success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now debugging becomes possible.&lt;/p&gt;

&lt;p&gt;Without tool-level observability, an agent failure often looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: Why did you give me the wrong answer?

Agent: Sorry.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is not an engineering strategy.&lt;/p&gt;




&lt;h1&gt;
  
  
  21. Version Your Tool Contracts
&lt;/h1&gt;

&lt;p&gt;Tool APIs evolve.&lt;/p&gt;

&lt;p&gt;Today:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tomorrow:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later:&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;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ranking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"semantic"&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;Changing semantics without considering existing agents can cause subtle failures.&lt;/p&gt;

&lt;p&gt;Treat tools like public APIs.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_documents.v1
search_documents.v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or maintain backward-compatible schemas where possible.&lt;/p&gt;

&lt;p&gt;This matters especially when multiple agents or external clients consume the same tool.&lt;/p&gt;




&lt;h1&gt;
  
  
  22. Test the Tool, Not Just the Model
&lt;/h1&gt;

&lt;p&gt;An agent can fail for two completely different reasons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model failure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool failure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to test both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool-level tests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_search_documents_rejects_empty_query&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_search_documents_rejects_invalid_limit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pricing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent-level tests
&lt;/h3&gt;

&lt;p&gt;Test whether the model chooses the right tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User:
"Find the pricing information in my uploaded files."

Expected:
search_documents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User:
"Search the internet for today's AI news."

Expected:
web_search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tool selection is part of agent behavior and should be evaluated as such.&lt;/p&gt;

&lt;p&gt;Modern agent development workflows increasingly include structured evaluation alongside unit and integration tests. Google's current agent tooling, for example, includes evaluation datasets and grading workflows as part of the development lifecycle. ([Google GitHub][2])&lt;/p&gt;




&lt;h1&gt;
  
  
  23. A Practical Tool Design Checklist
&lt;/h1&gt;

&lt;p&gt;Before exposing a function to an AI agent, ask:&lt;/p&gt;

&lt;h3&gt;
  
  
  Identity
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Is the tool name unambiguous?&lt;/li&gt;
&lt;li&gt;Does it describe a meaningful capability?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Does the description explain what the tool does?&lt;/li&gt;
&lt;li&gt;Does it explain when to use it?&lt;/li&gt;
&lt;li&gt;Does it explain when not to use it?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Inputs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Are parameters semantic?&lt;/li&gt;
&lt;li&gt;Are types explicit?&lt;/li&gt;
&lt;li&gt;Are required fields defined?&lt;/li&gt;
&lt;li&gt;Are constraints validated?&lt;/li&gt;
&lt;li&gt;Are ambiguous parameters avoided?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Outputs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Is the result structured?&lt;/li&gt;
&lt;li&gt;Can the agent easily understand what happened?&lt;/li&gt;
&lt;li&gt;Does the output provide enough information for the next decision?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Errors
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Are errors machine-readable?&lt;/li&gt;
&lt;li&gt;Can the agent distinguish retryable from permanent failures?&lt;/li&gt;
&lt;li&gt;Does the error explain what the agent can do next?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Safety
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Does the backend enforce authorization?&lt;/li&gt;
&lt;li&gt;Are destructive operations clearly identified?&lt;/li&gt;
&lt;li&gt;Are side effects explicit?&lt;/li&gt;
&lt;li&gt;Are confirmation requirements enforced outside the model?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reliability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Is the operation idempotent where appropriate?&lt;/li&gt;
&lt;li&gt;Can requests safely be retried?&lt;/li&gt;
&lt;li&gt;Are timeouts defined?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Observability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Can you trace every tool invocation?&lt;/li&gt;
&lt;li&gt;Can you identify latency and failures?&lt;/li&gt;
&lt;li&gt;Can you reproduce an agent run?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Evolution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Can the tool contract change safely?&lt;/li&gt;
&lt;li&gt;Do you have a versioning strategy?&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  24. The Architecture to Aim For
&lt;/h1&gt;

&lt;p&gt;A production agent should not look 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;LLM
 ↓
Random API calls
 ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A better architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                       ┌───────────────┐
                       │     User      │
                       └───────┬───────┘
                               ↓
                       ┌───────────────┐
                       │     Agent     │
                       └───────┬───────┘
                               ↓
                    ┌─────────────────────┐
                    │    Tool Registry    │
                    └─────────┬───────────┘
                              ↓
                 ┌────────────────────────┐
                 │ Agent-Friendly Tool API │
                 └────────────┬───────────┘
                              ↓
                  ┌────────────────────┐
                  │ Validation         │
                  │ Authorization      │
                  │ Rate Limits        │
                  │ Idempotency        │
                  │ Observability      │
                  └──────────┬─────────┘
                             ↓
                     ┌──────────────┐
                     │ Adapter Layer│
                     └──────┬───────┘
                            ↓
                ┌──────────────────────┐
                │ Internal APIs / DBs  │
                └──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model controls the reasoning loop.&lt;/p&gt;

&lt;p&gt;Your application controls the execution boundary.&lt;/p&gt;

&lt;p&gt;That separation is fundamental.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Real API Is the Model's Mental Model
&lt;/h1&gt;

&lt;p&gt;The biggest mistake in agent tool design is thinking of tools as simple wrappers around existing functions.&lt;/p&gt;

&lt;p&gt;They are not.&lt;/p&gt;

&lt;p&gt;A tool is a &lt;strong&gt;reasoning interface&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The model needs to understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What can I do?
When should I do it?
What information do I need?
What will happen?
What will I get back?
What should I do if it fails?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A well-designed tool API answers all six questions.&lt;/p&gt;

&lt;p&gt;This is why tool descriptions, schemas, structured outputs, error contracts, permissions, and observability matter so much.&lt;/p&gt;

&lt;p&gt;Protocols such as MCP are formalizing many of these concepts. Current MCP tooling supports tool descriptions, input schemas, output schemas, and behavioral annotations, while newer SDKs also provide schema-based validation for tool arguments and structured results. ([Model Context Protocol][1])&lt;/p&gt;

&lt;p&gt;The future of agent engineering is not just about giving models more tools.&lt;/p&gt;

&lt;p&gt;It is about giving them &lt;strong&gt;better interfaces to reason through&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And the difference between an unreliable agent and a production-grade agent may come down to something as small as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bad tool:
"execute_action"

Good tool:
"cancel_subscription"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second one gives the model something it can reason about.&lt;/p&gt;

&lt;p&gt;That is the real job of an agent tool API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;When designing tools for AI agents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Design for decisions.
Use explicit schemas.
Keep tools focused.
Write descriptions for the model.
Return structured results.
Make errors recoverable.
Separate reads from side effects.
Enforce authorization outside the model.
Support safe retries.
Instrument every invocation.
Evaluate tool selection.
Version important contracts.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The best tool API is not necessarily the one with the most capabilities.&lt;/p&gt;

&lt;p&gt;It is the one that gives the agent &lt;strong&gt;clear, constrained, predictable actions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is how you turn tool calling from a demo feature into an engineering system.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Context Engineering Is Becoming More Important Than Prompt Engineering</title>
      <dc:creator>Ali Raza</dc:creator>
      <pubDate>Thu, 17 Sep 2026 18:12:59 +0000</pubDate>
      <link>https://dev.to/ali_raza_fa80fd8371162ce6/context-engineering-is-becoming-more-important-than-prompt-engineering-32mo</link>
      <guid>https://dev.to/ali_raza_fa80fd8371162ce6/context-engineering-is-becoming-more-important-than-prompt-engineering-32mo</guid>
      <description>&lt;p&gt;For the first few years of generative AI, one skill dominated the conversation: &lt;strong&gt;prompt engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Developers learned how to write better instructions, structure prompts, provide examples, assign roles, specify output formats, and guide models toward better responses.&lt;/p&gt;

&lt;p&gt;That still matters.&lt;/p&gt;

&lt;p&gt;But as AI applications move beyond simple chat into coding assistants, research systems, autonomous agents, and tool-using workflows, another engineering problem is becoming harder to ignore:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What information should the model actually see at each step?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A good prompt cannot compensate for missing context.&lt;/p&gt;

&lt;p&gt;An intelligent model with the wrong context can still produce the wrong result.&lt;/p&gt;

&lt;p&gt;This is why &lt;strong&gt;context engineering&lt;/strong&gt; is emerging as an important discipline for developers building production AI systems.&lt;/p&gt;

&lt;p&gt;Anthropic describes context engineering as the broader practice of curating the information available to a model during inference, including system instructions, tools, external data, message history, and other relevant state. ([Anthropic][1])&lt;/p&gt;

&lt;p&gt;The shift is simple to describe:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prompt engineering asks, "What should I tell the model?"&lt;/p&gt;

&lt;p&gt;Context engineering asks, "What does the model need to know right now?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That difference becomes extremely important when building AI systems that operate over multiple steps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prompt Engineering Is Still Important
&lt;/h2&gt;

&lt;p&gt;Let's start with something clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt engineering is not dead.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clear instructions remain one of the simplest ways to improve model behavior.&lt;/p&gt;

&lt;p&gt;A developer might write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a senior Python developer.

Review the following function.

Identify:
1. Bugs
2. Security issues
3. Performance problems
4. Maintainability concerns

Return your answer using Markdown headings.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompt establishes the role, task, evaluation criteria, and output format.&lt;/p&gt;

&lt;p&gt;That is useful.&lt;/p&gt;

&lt;p&gt;Official OpenAI guidance continues to recommend clear instructions and structured prompting techniques for getting more useful model outputs. ([OpenAI Help Center][2])&lt;/p&gt;

&lt;p&gt;The problem appears when developers assume the prompt is the entire system.&lt;/p&gt;

&lt;p&gt;In a simple question-answering application, that assumption might work.&lt;/p&gt;

&lt;p&gt;In an agent that needs to inspect files, call APIs, remember previous actions, retrieve documents, and make decisions over several steps, it becomes much less effective.&lt;/p&gt;

&lt;p&gt;The model needs more than instructions.&lt;/p&gt;

&lt;p&gt;It needs &lt;strong&gt;state&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Is Context Engineering?
&lt;/h1&gt;

&lt;p&gt;Context engineering is the process of deciding &lt;strong&gt;what information enters the model's context, when it enters, how it is structured, and when it should be removed or replaced&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That context can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System instructions&lt;/li&gt;
&lt;li&gt;User messages&lt;/li&gt;
&lt;li&gt;Conversation history&lt;/li&gt;
&lt;li&gt;Retrieved documents&lt;/li&gt;
&lt;li&gt;Database records&lt;/li&gt;
&lt;li&gt;Tool definitions&lt;/li&gt;
&lt;li&gt;Tool results&lt;/li&gt;
&lt;li&gt;User preferences&lt;/li&gt;
&lt;li&gt;Application state&lt;/li&gt;
&lt;li&gt;Previous actions&lt;/li&gt;
&lt;li&gt;Code files&lt;/li&gt;
&lt;li&gt;Error messages&lt;/li&gt;
&lt;li&gt;External knowledge&lt;/li&gt;
&lt;li&gt;Agent memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anthropic's engineering team describes context as a finite resource and argues that effective agent systems should focus on supplying the smallest set of high-signal information needed for the desired outcome. ([Anthropic][1])&lt;/p&gt;

&lt;p&gt;This changes the developer's job.&lt;/p&gt;

&lt;p&gt;Instead of writing one giant prompt containing everything, developers need to build systems that &lt;strong&gt;assemble the right context dynamically&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Bigger Context Is Not Always Better
&lt;/h1&gt;

&lt;p&gt;A common assumption is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If more context helps, then giving the model everything should help even more."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sounds logical.&lt;/p&gt;

&lt;p&gt;It is often wrong.&lt;/p&gt;

&lt;p&gt;More information can create noise.&lt;/p&gt;

&lt;p&gt;Imagine asking an AI coding agent to fix a bug in one authentication function.&lt;/p&gt;

&lt;p&gt;You could provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The entire repository&lt;/li&gt;
&lt;li&gt;Every README&lt;/li&gt;
&lt;li&gt;Every previous conversation&lt;/li&gt;
&lt;li&gt;All database schemas&lt;/li&gt;
&lt;li&gt;All logs from the last six months&lt;/li&gt;
&lt;li&gt;Every API specification&lt;/li&gt;
&lt;li&gt;Every dependency document&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model now has an enormous amount of information.&lt;/p&gt;

&lt;p&gt;But most of it is irrelevant.&lt;/p&gt;

&lt;p&gt;The actual bug may depend on three files and one recent error message.&lt;/p&gt;

&lt;p&gt;The developer's job is therefore not simply to maximize context.&lt;/p&gt;

&lt;p&gt;It is to maximize &lt;strong&gt;relevant context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Anthropic refers to this challenge in terms of an attention budget and notes that model performance can degrade as context becomes increasingly crowded with information. ([Anthropic][1])&lt;/p&gt;

&lt;p&gt;The goal is not:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More tokens.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The goal is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More useful tokens.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Context Engineering vs Prompt Engineering
&lt;/h1&gt;

&lt;p&gt;The easiest way to understand the difference is to compare their responsibilities.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prompt Engineering&lt;/th&gt;
&lt;th&gt;Context Engineering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Writes instructions&lt;/td&gt;
&lt;td&gt;Curates information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defines desired behavior&lt;/td&gt;
&lt;td&gt;Defines available state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optimizes wording&lt;/td&gt;
&lt;td&gt;Optimizes information selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Often static&lt;/td&gt;
&lt;td&gt;Often dynamic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on prompts&lt;/td&gt;
&lt;td&gt;Focuses on the entire context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Useful for individual tasks&lt;/td&gt;
&lt;td&gt;Critical for multi-step systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defines what to do&lt;/td&gt;
&lt;td&gt;Helps determine what to know&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A prompt might tell an agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this pull request and identify potential bugs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Context engineering determines whether the agent receives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pull request
+
Changed files
+
Relevant surrounding code
+
Existing tests
+
Project conventions
+
Related issue
+
Recent CI failures
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt gives the instruction.&lt;/p&gt;

&lt;p&gt;The context gives the agent the information required to execute that instruction intelligently.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Context Window Is an Engineering Constraint
&lt;/h1&gt;

&lt;p&gt;Developers often talk about context windows as if they were simply storage limits.&lt;/p&gt;

&lt;p&gt;They are more than that.&lt;/p&gt;

&lt;p&gt;Even when a model supports a large context window, the engineering problem remains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which information deserves the model's attention?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider an AI coding agent working on a large repository.&lt;/p&gt;

&lt;p&gt;At the beginning, it might need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project architecture&lt;/li&gt;
&lt;li&gt;Developer instructions&lt;/li&gt;
&lt;li&gt;Relevant source files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After discovering a bug, it might need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error logs&lt;/li&gt;
&lt;li&gt;A related function&lt;/li&gt;
&lt;li&gt;Test cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After making a change, it might need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The modified files&lt;/li&gt;
&lt;li&gt;Test output&lt;/li&gt;
&lt;li&gt;Compiler errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The optimal context changes throughout the task.&lt;/p&gt;

&lt;p&gt;This means context engineering is inherently &lt;strong&gt;dynamic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The model does not need the same information at every step.&lt;/p&gt;




&lt;h1&gt;
  
  
  Retrieval Is Part of Context Engineering
&lt;/h1&gt;

&lt;p&gt;This is where retrieval systems become important.&lt;/p&gt;

&lt;p&gt;A traditional RAG system might retrieve documents based on the user's question and place those documents into the model's context.&lt;/p&gt;

&lt;p&gt;That approach works well for many applications.&lt;/p&gt;

&lt;p&gt;But agentic systems introduce another possibility.&lt;/p&gt;

&lt;p&gt;Instead of loading everything up front, an agent can retrieve information when it becomes relevant.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User request
     ↓
Agent identifies problem
     ↓
Search relevant files
     ↓
Read selected files
     ↓
Analyze error
     ↓
Search related implementation
     ↓
Run tests
     ↓
Inspect results
     ↓
Modify code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is different from dumping an entire repository into the initial prompt.&lt;/p&gt;

&lt;p&gt;Anthropic describes this approach as "just in time" context retrieval, where agents maintain lightweight references and load information dynamically through tools when needed. ([Anthropic][1])&lt;/p&gt;

&lt;p&gt;For large systems, that can be a much more scalable architecture.&lt;/p&gt;




&lt;h1&gt;
  
  
  Tools Are Also Context
&lt;/h1&gt;

&lt;p&gt;One of the most interesting parts of context engineering is that &lt;strong&gt;tools themselves become part of the model's available context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Consider an agent with these tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search_database()
read_file()
write_file()
run_tests()
send_email()
delete_record()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model does not simply need to know that these tools exist.&lt;/p&gt;

&lt;p&gt;It needs to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What each tool does&lt;/li&gt;
&lt;li&gt;When to use it&lt;/li&gt;
&lt;li&gt;What arguments it requires&lt;/li&gt;
&lt;li&gt;What it returns&lt;/li&gt;
&lt;li&gt;What its limitations are&lt;/li&gt;
&lt;li&gt;Whether the action is reversible&lt;/li&gt;
&lt;li&gt;What permissions it requires&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenAI's agent guidance emphasizes the importance of well-defined tools for agents, including tools for retrieving information and tools for taking actions. ([OpenAI][3])&lt;/p&gt;

&lt;p&gt;Anthropic has similarly noted that tool descriptions are loaded into an agent's context and that precise descriptions can influence tool-calling behavior. ([Anthropic][4])&lt;/p&gt;

&lt;p&gt;This means tool design is not separate from context design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your API documentation can become part of the agent's reasoning environment.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  MCP Makes This Even More Interesting
&lt;/h1&gt;

&lt;p&gt;The rise of the Model Context Protocol provides another example of why context engineering is becoming an architectural concern.&lt;/p&gt;

&lt;p&gt;MCP defines standardized ways for applications to expose &lt;strong&gt;prompts, resources, and tools&lt;/strong&gt; to AI systems. Resources can provide contextual data such as files or database schemas, while tools can allow models to retrieve information or perform actions. ([Model Context Protocol][5])&lt;/p&gt;

&lt;p&gt;This creates a more structured relationship between models and external systems.&lt;/p&gt;

&lt;p&gt;Instead of building every integration as a completely custom mechanism, applications can expose standardized capabilities.&lt;/p&gt;

&lt;p&gt;But that creates another engineering question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which resources and tools should be exposed to the model at a particular moment?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Giving an agent access to 100 tools does not automatically make it more capable.&lt;/p&gt;

&lt;p&gt;It can make the decision space more complicated.&lt;/p&gt;

&lt;p&gt;Tool selection therefore becomes part of context engineering.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Problem of Context Pollution
&lt;/h1&gt;

&lt;p&gt;Long-running agents create another challenge.&lt;/p&gt;

&lt;p&gt;Imagine an agent working for an hour.&lt;/p&gt;

&lt;p&gt;During that time, it generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User messages&lt;/li&gt;
&lt;li&gt;Tool calls&lt;/li&gt;
&lt;li&gt;Tool results&lt;/li&gt;
&lt;li&gt;Intermediate reasoning&lt;/li&gt;
&lt;li&gt;Errors&lt;/li&gt;
&lt;li&gt;Search results&lt;/li&gt;
&lt;li&gt;File contents&lt;/li&gt;
&lt;li&gt;Test results&lt;/li&gt;
&lt;li&gt;Decisions&lt;/li&gt;
&lt;li&gt;Temporary observations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually, the context can become crowded.&lt;/p&gt;

&lt;p&gt;Some information is still important.&lt;/p&gt;

&lt;p&gt;Some information is outdated.&lt;/p&gt;

&lt;p&gt;Some information is duplicated.&lt;/p&gt;

&lt;p&gt;Some information is completely irrelevant.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;context pollution&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the system simply keeps appending everything, the model may have difficulty identifying the information that matters most.&lt;/p&gt;

&lt;p&gt;The solution is not necessarily a larger context window.&lt;/p&gt;

&lt;p&gt;Instead, developers can use strategies such as:&lt;/p&gt;

&lt;h3&gt;
  
  
  Compaction
&lt;/h3&gt;

&lt;p&gt;Summarize older interactions and replace them with a more concise representation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structured notes
&lt;/h3&gt;

&lt;p&gt;Store important discoveries separately from temporary conversation history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Selective retrieval
&lt;/h3&gt;

&lt;p&gt;Retrieve information again when it becomes relevant instead of carrying it through the entire interaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  State management
&lt;/h3&gt;

&lt;p&gt;Keep application state outside the conversation and inject only the necessary portion when required.&lt;/p&gt;

&lt;p&gt;Anthropic discusses compaction, structured note-taking, and multi-agent approaches as strategies for handling long-horizon agent tasks. ([Anthropic][1])&lt;/p&gt;




&lt;h1&gt;
  
  
  Context Should Be Treated Like a System Resource
&lt;/h1&gt;

&lt;p&gt;Developers already think carefully about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;li&gt;Memory&lt;/li&gt;
&lt;li&gt;Network bandwidth&lt;/li&gt;
&lt;li&gt;Database connections&lt;/li&gt;
&lt;li&gt;Cache usage&lt;/li&gt;
&lt;li&gt;Storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Context deserves similar treatment.&lt;/p&gt;

&lt;p&gt;You should know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What enters the context?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does it enter?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long should it remain?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who controls it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can it become stale?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when it becomes too large?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is especially important for production AI systems.&lt;/p&gt;

&lt;p&gt;A context pipeline might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Request
     ↓
Intent Detection
     ↓
Relevant State
     ↓
Retrieval
     ↓
Tool Selection
     ↓
Context Assembly
     ↓
Model
     ↓
Tool Result
     ↓
Context Update
     ↓
Next Model Step
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much closer to software architecture than simple prompt writing.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Practical Context Engineering Architecture
&lt;/h1&gt;

&lt;p&gt;Suppose you're building an AI support agent.&lt;/p&gt;

&lt;p&gt;Instead of creating one massive prompt, divide the context into layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Stable instructions
&lt;/h3&gt;

&lt;p&gt;Things that rarely change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a customer support agent.
Follow company policies.
Never expose private customer information.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 2: User context
&lt;/h3&gt;

&lt;p&gt;Information about the current customer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer ID: 12345
Plan: Pro
Account age: 2 years
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 3: Task context
&lt;/h3&gt;

&lt;p&gt;What the customer currently needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Issue: Payment failed
Previous attempts: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 4: Retrieved context
&lt;/h3&gt;

&lt;p&gt;Relevant documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment troubleshooting guide
Refund policy
Current billing status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 5: Tool context
&lt;/h3&gt;

&lt;p&gt;Available actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;check_payment()
create_ticket()
issue_refund()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 6: Current state
&lt;/h3&gt;

&lt;p&gt;What happened during the current task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment provider returned error 402.
Customer has already retried twice.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This layered approach makes the system easier to reason about and debug.&lt;/p&gt;




&lt;h1&gt;
  
  
  Context Engineering Changes How We Debug AI Systems
&lt;/h1&gt;

&lt;p&gt;Traditional debugging asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why did the code produce this output?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI systems require another question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What information did the model have when it produced this output?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means production observability should capture context-related signals.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request ID
Model
Prompt version
Retrieved documents
Tool definitions
Tool calls
Tool results
Context size
Output
Evaluation result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://goodoff.co/" rel="noopener noreferrer"&gt;https://goodoff.co/&lt;/a&gt;&lt;br&gt;
If an agent makes a bad decision, developers need to know whether:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The model misunderstood the instruction&lt;/li&gt;
&lt;li&gt;The wrong document was retrieved&lt;/li&gt;
&lt;li&gt;Important context was missing&lt;/li&gt;
&lt;li&gt;Irrelevant context dominated the request&lt;/li&gt;
&lt;li&gt;A tool returned incorrect information&lt;/li&gt;
&lt;li&gt;The context contained stale information&lt;/li&gt;
&lt;li&gt;The tool description was ambiguous&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this visibility, debugging becomes guesswork.&lt;/p&gt;




&lt;h1&gt;
  
  
  Context Engineering Is Also About Security
&lt;/h1&gt;

&lt;p&gt;Context is not just a performance concern.&lt;/p&gt;

&lt;p&gt;It is a security boundary.&lt;/p&gt;

&lt;p&gt;If an agent receives sensitive information that it does not need, the risk increases.&lt;/p&gt;

&lt;p&gt;Consider an internal enterprise agent.&lt;/p&gt;

&lt;p&gt;It might have access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer records&lt;/li&gt;
&lt;li&gt;Financial information&lt;/li&gt;
&lt;li&gt;Employee data&lt;/li&gt;
&lt;li&gt;Internal documentation&lt;/li&gt;
&lt;li&gt;Private source code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The correct question is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can the AI access all of this?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What does the AI need to access for this specific task?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The principle of least privilege applies to context just as it applies to traditional software permissions.&lt;/p&gt;

&lt;p&gt;MCP's specification also includes security considerations around validating resource identifiers and implementing access controls for sensitive resources. ([Model Context Protocol][5])&lt;/p&gt;

&lt;p&gt;Good context engineering therefore means giving an agent enough information to work effectively without unnecessarily exposing everything available.&lt;/p&gt;




&lt;h1&gt;
  
  
  How Developers Should Think About Context Engineering
&lt;/h1&gt;

&lt;p&gt;A useful mental model is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt = instructions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context = working environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools = capabilities&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory = persistent state&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieval = information selection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model = reasoning engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you think about AI systems this way, many architectural decisions become clearer.&lt;/p&gt;

&lt;p&gt;The model is not operating in isolation.&lt;/p&gt;

&lt;p&gt;Its behavior emerges from the combination of the model and the environment you construct around it.&lt;/p&gt;

&lt;p&gt;That environment is increasingly becoming the real engineering challenge.&lt;/p&gt;




&lt;h1&gt;
  
  
  Five Practical Rules for Better Context Engineering
&lt;/h1&gt;

&lt;h3&gt;
  
  
  1. Give the model the smallest useful context
&lt;/h3&gt;

&lt;p&gt;Do not automatically include everything.&lt;/p&gt;

&lt;p&gt;Start with high-signal information.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Retrieve information when it becomes relevant
&lt;/h3&gt;

&lt;p&gt;Dynamic retrieval can be better than loading large datasets upfront.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Keep instructions separate from data
&lt;/h3&gt;

&lt;p&gt;Clearly distinguish rules, user information, retrieved content, and tool outputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Treat tool descriptions as part of the AI interface
&lt;/h3&gt;

&lt;p&gt;Poorly documented tools can create poor agent behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Measure context, not just output
&lt;/h3&gt;

&lt;p&gt;Track what information the model received when evaluating failures.&lt;/p&gt;

&lt;p&gt;These principles are simple, but they can significantly change how AI applications are designed.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Future of AI Engineering Is Bigger Than Prompting
&lt;/h1&gt;

&lt;p&gt;Prompt engineering became important because developers discovered that language models respond differently depending on how instructions are expressed.&lt;/p&gt;

&lt;p&gt;Context engineering takes the next step.&lt;/p&gt;

&lt;p&gt;It asks developers to design the &lt;strong&gt;information environment in which the model operates&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As AI applications become more agentic, that environment becomes increasingly dynamic.&lt;/p&gt;

&lt;p&gt;The model may need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search a database&lt;/li&gt;
&lt;li&gt;Read a document&lt;/li&gt;
&lt;li&gt;Inspect code&lt;/li&gt;
&lt;li&gt;Call an API&lt;/li&gt;
&lt;li&gt;Remember a previous decision&lt;/li&gt;
&lt;li&gt;Check a policy&lt;/li&gt;
&lt;li&gt;Run a test&lt;/li&gt;
&lt;li&gt;Observe the result&lt;/li&gt;
&lt;li&gt;Change its next action&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of those steps can change the context.&lt;/p&gt;

&lt;p&gt;That makes context engineering less like writing a clever prompt and more like designing a runtime system.&lt;/p&gt;

&lt;p&gt;The best AI application may not be the one with the longest prompt.&lt;/p&gt;

&lt;p&gt;It may be the one that consistently gives the model &lt;strong&gt;the right information at the right time&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Prompt engineering taught developers how to communicate with language models.&lt;/p&gt;

&lt;p&gt;Context engineering is teaching developers how to build the environment around them.&lt;/p&gt;

&lt;p&gt;That distinction matters because modern AI systems are no longer limited to answering isolated questions.&lt;/p&gt;

&lt;p&gt;They are becoming systems that retrieve information, use tools, maintain state, execute multi-step workflows, and operate for extended periods.&lt;/p&gt;

&lt;p&gt;In that world, a perfect prompt is not enough.&lt;/p&gt;

&lt;p&gt;The model needs relevant information.&lt;/p&gt;

&lt;p&gt;It needs the right tools.&lt;/p&gt;

&lt;p&gt;It needs useful state.&lt;/p&gt;

&lt;p&gt;It needs reliable retrieval.&lt;/p&gt;

&lt;p&gt;It needs protection from irrelevant or sensitive information.&lt;/p&gt;

&lt;p&gt;And it needs a context that changes as the task changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The future of AI engineering is not about finding one perfect prompt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is about building systems that know &lt;strong&gt;what the model needs to know, when it needs to know it, and when it no longer needs to know it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is why context engineering is becoming one of the most important ideas in modern AI application development.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
