<?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: Lee</title>
    <description>The latest articles on DEV Community by Lee (@kevinpruett023_kevinpruet).</description>
    <link>https://dev.to/kevinpruett023_kevinpruet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4027960%2Fe04ebb48-0acc-482a-8531-6ff383d97e2b.webp</url>
      <title>DEV Community: Lee</title>
      <link>https://dev.to/kevinpruett023_kevinpruet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kevinpruett023_kevinpruet"/>
    <language>en</language>
    <item>
      <title>The AI Model Is Not the Product: What Actually Makes an AI System Production-Ready</title>
      <dc:creator>Lee</dc:creator>
      <pubDate>Fri, 02 Oct 2026 11:37:24 +0000</pubDate>
      <link>https://dev.to/kevinpruett023_kevinpruet/the-ai-model-is-not-the-product-what-actually-makes-an-ai-system-production-ready-4p1e</link>
      <guid>https://dev.to/kevinpruett023_kevinpruet/the-ai-model-is-not-the-product-what-actually-makes-an-ai-system-production-ready-4p1e</guid>
      <description>&lt;p&gt;There is a common misconception in AI engineering:&lt;/p&gt;

&lt;p&gt;If you have a powerful model, you have a powerful AI product.&lt;/p&gt;

&lt;p&gt;In my experience, that is rarely true.&lt;/p&gt;

&lt;p&gt;A model can generate impressive responses in a notebook and still become unreliable when connected to real users, real data, APIs, databases, and business processes.&lt;/p&gt;

&lt;p&gt;The model is only one component of the system.&lt;/p&gt;

&lt;p&gt;The Real AI Stack&lt;/p&gt;

&lt;p&gt;A production AI application usually looks more 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
  ↓
API / Application
  ↓
Authentication &amp;amp; Validation
  ↓
AI Orchestration
  ↓
 ┌───────────────┬───────────────┐
 │               │               │
 LLM           Retrieval        Tools
 │               │               │
 └───────────────┴───────────────┘
                 ↓
          Business Logic
                 ↓
             Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting engineering problems are between these components.&lt;/p&gt;




&lt;p&gt;A Model Can Be Correct and the System Can Still Fail&lt;/p&gt;

&lt;p&gt;Imagine an AI assistant that helps customers modify an order.&lt;/p&gt;

&lt;p&gt;The LLM correctly understands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Change my order to 10 units."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It generates the correct tool call.&lt;/p&gt;

&lt;p&gt;But the application sends the wrong customer ID to the backend.&lt;/p&gt;

&lt;p&gt;The model wasn't the problem.&lt;/p&gt;

&lt;p&gt;The system was.&lt;/p&gt;

&lt;p&gt;This is why I prefer investigating AI failures across multiple layers instead of automatically blaming the model.&lt;/p&gt;




&lt;p&gt;Python Makes the Architecture Easier to Control&lt;/p&gt;

&lt;p&gt;A clean Python service might expose a small interface:&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;class&lt;/span&gt; &lt;span class="nc"&gt;AIService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve_context&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;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;request&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;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&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="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part isn't the exact implementation.&lt;/p&gt;

&lt;p&gt;It's the separation of responsibilities.&lt;/p&gt;

&lt;p&gt;Each stage can be tested independently.&lt;/p&gt;

&lt;p&gt;That becomes extremely valuable once the application grows.&lt;/p&gt;




&lt;p&gt;Don't Let the LLM Decide Everything&lt;/p&gt;

&lt;p&gt;I have seen systems where developers put business rules directly into prompts.&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 plaintext"&gt;&lt;code&gt;If the customer is eligible and the order
is below the allowed limit, approve it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That might work initially.&lt;/p&gt;

&lt;p&gt;But critical business rules should live in deterministic code.&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;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verified&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;customer&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="nf"&gt;approve_order&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;require_review&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM can interpret language.&lt;/p&gt;

&lt;p&gt;Your application should enforce the rules.&lt;/p&gt;

&lt;p&gt;That distinction becomes especially important when money, permissions, customer data, or external actions are involved.&lt;/p&gt;




&lt;p&gt;Observability Is Part of the AI Architecture&lt;/p&gt;

&lt;p&gt;With traditional APIs, logging a request and response may be enough.&lt;/p&gt;

&lt;p&gt;AI applications need more context.&lt;/p&gt;

&lt;p&gt;For every important request, I want to know:&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 Context
Tool Calls
Tool Results
Latency
Token Usage
Validation Result
Final Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, debugging becomes:&lt;/p&gt;

&lt;p&gt;"The AI gave the wrong answer."&lt;/p&gt;

&lt;p&gt;That's not enough information to fix anything.&lt;/p&gt;

&lt;p&gt;A good AI system should allow engineers to reconstruct the execution path.&lt;/p&gt;




&lt;p&gt;Evaluation Should Happen Before Deployment&lt;/p&gt;

&lt;p&gt;One of the biggest changes in my approach to AI development has been treating evaluation as part of the development lifecycle.&lt;/p&gt;

&lt;p&gt;Instead of manually testing five examples, build a dataset.&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="n"&gt;test_cases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&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;Cancel my order&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;expected_intent&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;cancel_order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&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;Where is my package?&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;expected_intent&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;track_order&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the dataset whenever you change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the model&lt;/li&gt;
&lt;li&gt;prompts&lt;/li&gt;
&lt;li&gt;retrieval&lt;/li&gt;
&lt;li&gt;tool definitions&lt;/li&gt;
&lt;li&gt;application logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This turns AI development into an iterative engineering process rather than subjective experimentation.&lt;/p&gt;




&lt;p&gt;Production AI Needs Failure Modes&lt;/p&gt;

&lt;p&gt;What happens when the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;times out?&lt;/li&gt;
&lt;li&gt;returns malformed JSON?&lt;/li&gt;
&lt;li&gt;selects the wrong tool?&lt;/li&gt;
&lt;li&gt;receives incomplete context?&lt;/li&gt;
&lt;li&gt;produces an unsafe response?&lt;/li&gt;
&lt;li&gt;exceeds the token budget?&lt;/li&gt;
&lt;li&gt;encounters an unavailable API?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A production system needs explicit answers.&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ai_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&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="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;TimeoutError&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;fallback_response&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;ValidationError&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;retry_with_constraints&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact strategy depends on the application.&lt;/p&gt;

&lt;p&gt;The principle doesn't.&lt;/p&gt;

&lt;p&gt;"Failure should be designed, not discovered in production."&lt;/p&gt;




&lt;p&gt;Cost Is Also an Engineering Problem&lt;/p&gt;

&lt;p&gt;A system that works perfectly but costs 10x more than expected isn't necessarily a successful system.&lt;/p&gt;

&lt;p&gt;I usually think about AI requests in terms of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Quality
Latency
Cost
Reliability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Improving one can negatively affect another.&lt;/p&gt;

&lt;p&gt;For example, sending the entire conversation and a large document to an expensive model might improve context—but dramatically increase cost and latency.&lt;/p&gt;

&lt;p&gt;Sometimes the better solution is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Better retrieval
      ↓
Smaller context
      ↓
Smaller model
      ↓
Lower latency
      ↓
Lower cost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optimization often starts before the model call.&lt;/p&gt;




&lt;p&gt;The Most Important Question&lt;/p&gt;

&lt;p&gt;When building an AI feature, I don't start with:&lt;/p&gt;

&lt;p&gt;"Which model should we use?"&lt;/p&gt;

&lt;p&gt;I start with:&lt;/p&gt;

&lt;p&gt;"What should this system do when the model is wrong?"&lt;/p&gt;

&lt;p&gt;That question changes the architecture.&lt;/p&gt;

&lt;p&gt;It leads naturally to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;observability&lt;/li&gt;
&lt;li&gt;evaluation&lt;/li&gt;
&lt;li&gt;deterministic business logic&lt;/li&gt;
&lt;li&gt;fallbacks&lt;/li&gt;
&lt;li&gt;permissions&lt;/li&gt;
&lt;li&gt;monitoring&lt;/li&gt;
&lt;li&gt;clear service boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model is important.&lt;/p&gt;

&lt;p&gt;But production reliability comes from everything surrounding the model.&lt;/p&gt;

&lt;p&gt;Final Thought&lt;/p&gt;

&lt;p&gt;AI engineering is gradually becoming less about simply calling an LLM and more about designing reliable software around probabilistic components.&lt;/p&gt;

&lt;p&gt;Python gives us excellent tools for building that software.&lt;/p&gt;

&lt;p&gt;FastAPI, async processing, databases, queues, testing frameworks, observability tools, and clean service abstractions all become part of the AI engineering toolbox.&lt;/p&gt;

&lt;p&gt;The strongest AI systems I've worked toward are not the ones where the model appears to do everything.&lt;/p&gt;

&lt;p&gt;They are the ones where the model has "exactly the amount of responsibility it should have—and no more."&lt;/p&gt;

&lt;p&gt;That's where AI experimentation starts becoming engineering.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>From Prototype to Production: What I Learned Building Reliable AI Systems with Python</title>
      <dc:creator>Lee</dc:creator>
      <pubDate>Fri, 02 Oct 2026 09:09:00 +0000</pubDate>
      <link>https://dev.to/kevinpruett023_kevinpruet/from-prototype-to-production-what-i-learned-building-reliable-ai-systems-with-python-435j</link>
      <guid>https://dev.to/kevinpruett023_kevinpruet/from-prototype-to-production-what-i-learned-building-reliable-ai-systems-with-python-435j</guid>
      <description>&lt;p&gt;AI development is deceptively easy at the beginning.&lt;/p&gt;

&lt;p&gt;You can connect an LLM to a Python application in a few hours. Add a prompt, expose an API, connect a database, and suddenly you have something that looks like an AI product.&lt;/p&gt;

&lt;p&gt;The difficult part starts afterward.&lt;/p&gt;

&lt;p&gt;The real engineering challenge is not getting an LLM to produce an answer. It is building a system that remains "predictable, observable, testable, and maintainable when the model is wrong".&lt;/p&gt;

&lt;p&gt;After working with Python-based AI systems, I have learned that production AI requires a different mindset from traditional application development.&lt;/p&gt;

&lt;p&gt;Here are some of the lessons that have mattered most.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The LLM Should Not Own Your Business Logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the first mistakes I see in AI applications is putting too much responsibility inside the prompt.&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="n"&gt;prompt&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 the customer request and decide what action should be taken.
If necessary, call the appropriate tool.
Return the final response.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works during a demo.&lt;/p&gt;

&lt;p&gt;But eventually you need deterministic rules.&lt;/p&gt;

&lt;p&gt;Instead, I prefer separating the system into clear layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API
 ↓
Application Logic
 ↓
AI Decision Layer
 ↓
Tools / Services
 ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can make decisions, classify intent, extract information, or choose a tool.&lt;/p&gt;

&lt;p&gt;It should not silently become the source of truth for critical business rules.&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;if&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;is_verified&lt;/span&gt; &lt;span class="ow"&gt;and&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;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;transaction_limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;process_transaction&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;require_manual_review&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That rule belongs in application code, not in a prompt.&lt;/p&gt;

&lt;p&gt;The LLM should assist the system—not secretly become the system.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;AI Systems Need Evaluation, Not Just Unit Tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traditional software often has relatively deterministic outputs.&lt;/p&gt;

&lt;p&gt;AI doesn't.&lt;/p&gt;

&lt;p&gt;If the input is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Can I cancel my reservation?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;there may be several acceptable responses.&lt;/p&gt;

&lt;p&gt;That makes testing more interesting.&lt;/p&gt;

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

&lt;p&gt;"Did the function return the expected string?"&lt;/p&gt;

&lt;p&gt;we need to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was the correct intent detected?&lt;/li&gt;
&lt;li&gt;Was the correct tool selected?&lt;/li&gt;
&lt;li&gt;Were required parameters extracted?&lt;/li&gt;
&lt;li&gt;Was sensitive information exposed?&lt;/li&gt;
&lt;li&gt;Was the response grounded in available data?&lt;/li&gt;
&lt;li&gt;Did the model follow the application's constraints?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like thinking about evaluation as a separate engineering layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    AI System
                       │
              ┌────────┴────────┐
              │                 │
         Application          Evaluation
              │                 │
        Production Data     Test Dataset
              │                 │
              └────────┬────────┘
                       ↓
                  Metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A model change should not be considered successful simply because the new model "feels better."&lt;/p&gt;

&lt;p&gt;We need evidence.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Build a Dataset From Real Failures&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the most valuable things an AI team can build is not another prompt.&lt;/p&gt;

&lt;p&gt;It is a good evaluation dataset.&lt;/p&gt;

&lt;p&gt;Whenever the system fails, capture the scenario.&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;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I want to change my booking to 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;"expected_intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"modify_booking"&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_tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update_booking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"critical_constraints"&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;"verify_booking_owner"&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;Over time, these failures become regression tests.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;the model&lt;/li&gt;
&lt;li&gt;system prompt&lt;/li&gt;
&lt;li&gt;retrieval strategy&lt;/li&gt;
&lt;li&gt;tool definitions&lt;/li&gt;
&lt;li&gt;temperature&lt;/li&gt;
&lt;li&gt;context window&lt;/li&gt;
&lt;li&gt;agent workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;you can measure whether the system actually improved.&lt;/p&gt;

&lt;p&gt;This changes AI development from:&lt;/p&gt;

&lt;p&gt;"I think this prompt is better."&lt;/p&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;p&gt;"This change improved task success from 87% to 93% across our evaluation set."&lt;/p&gt;

&lt;p&gt;That is a much healthier engineering process.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Python Makes AI Orchestration Extremely Practical&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Python has become particularly effective for AI applications because the ecosystem makes experimentation fast.&lt;/p&gt;

&lt;p&gt;A typical architecture 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;FastAPI
   │
   ├── Authentication
   ├── Request validation
   └── API endpoints
          │
          ↓
     AI Orchestrator
          │
     ┌────┼────┐
     ↓    ↓    ↓
   LLM  RAG  Tools
     │    │    │
     └────┼────┘
          ↓
      PostgreSQL
&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 python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&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;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask&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;UserRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;context&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;retrieve_context&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;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ai_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;message&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;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&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;decision&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part isn't FastAPI itself.&lt;/p&gt;

&lt;p&gt;The important part is keeping the AI layer behind a clean interface.&lt;/p&gt;

&lt;p&gt;That allows the underlying model or provider to change without rewriting the entire application.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Model Abstraction Is More Important Than It Looks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AI infrastructure changes quickly.&lt;/p&gt;

&lt;p&gt;A model that works well today may not be the best choice six months later.&lt;/p&gt;

&lt;p&gt;I therefore prefer interfaces such as:&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;class&lt;/span&gt; &lt;span class="nc"&gt;LLMProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;NotImplementedError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then implementations can be swapped behind the interface.&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;class&lt;/span&gt; &lt;span class="nc"&gt;ProviderA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LLMProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProviderB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LLMProvider&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not about blindly supporting every model provider.&lt;/p&gt;

&lt;p&gt;It is about avoiding unnecessary coupling.&lt;/p&gt;

&lt;p&gt;Your application should depend on an abstraction.&lt;/p&gt;

&lt;p&gt;Not on a specific model API scattered across 40 files.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Observability Becomes Critical Once AI Enters Production&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traditional logs are not enough.&lt;/p&gt;

&lt;p&gt;For an AI request, I want to understand the complete execution path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
  ↓
Retrieved Context
  ↓
Prompt Version
  ↓
Model
  ↓
Tool Calls
  ↓
Tool Results
  ↓
Final Response
  ↓
Latency / Cost / Evaluation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A production AI system should make it possible to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why did the model produce this answer?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not perfectly—LLMs are probabilistic—but enough to reconstruct what happened.&lt;/p&gt;

&lt;p&gt;Useful telemetry includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request ID&lt;/li&gt;
&lt;li&gt;model/provider&lt;/li&gt;
&lt;li&gt;prompt version&lt;/li&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;token usage&lt;/li&gt;
&lt;li&gt;retrieved documents&lt;/li&gt;
&lt;li&gt;tool calls&lt;/li&gt;
&lt;li&gt;tool results&lt;/li&gt;
&lt;li&gt;validation failures&lt;/li&gt;
&lt;li&gt;fallback events&lt;/li&gt;
&lt;li&gt;evaluation scores&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this information, debugging AI behavior becomes guesswork.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Guardrails Should Exist Outside the Prompt&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A common pattern is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Never reveal confidential information."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside the system prompt.&lt;/p&gt;

&lt;p&gt;That's useful, but it shouldn't be the only protection.&lt;/p&gt;

&lt;p&gt;Critical constraints should be enforced programmatically.&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="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;contains_sensitive_data&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;redact_sensitive_data&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;passes_policy&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fallback_response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The general principle is simple:&lt;/p&gt;

&lt;p&gt;"Prompts provide instructions. Code provides enforcement."&lt;/p&gt;

&lt;p&gt;That distinction becomes increasingly important as AI systems gain access to real tools and data.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Agents Need Boundaries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Agentic systems are powerful because they allow an LLM to reason through multiple steps.&lt;/p&gt;

&lt;p&gt;But giving an agent unlimited freedom is usually a bad engineering decision.&lt;/p&gt;

&lt;p&gt;I prefer explicit boundaries:&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;MAX_STEPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;step&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;MAX_STEPS&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next_action&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;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_final&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="n"&gt;result&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;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="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;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which tools the agent can access&lt;/li&gt;
&lt;li&gt;how many steps it can execute&lt;/li&gt;
&lt;li&gt;what data it can read&lt;/li&gt;
&lt;li&gt;what actions require confirmation&lt;/li&gt;
&lt;li&gt;what happens when a tool fails&lt;/li&gt;
&lt;li&gt;when the system should stop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An agent without boundaries is difficult to reason about.&lt;/p&gt;

&lt;p&gt;An agent with explicit constraints becomes an engineering component.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Retrieval Quality Often Matters More Than Prompt Quality&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When building RAG systems, teams sometimes spend hours rewriting prompts while ignoring retrieval.&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;User Question
     ↓
Retriever
     ↓
Wrong Documents
     ↓
Perfect Prompt
     ↓
Wrong Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model cannot reliably answer a question using context that was never retrieved.&lt;/p&gt;

&lt;p&gt;I therefore measure retrieval independently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;precision&lt;/li&gt;
&lt;li&gt;recall&lt;/li&gt;
&lt;li&gt;relevance&lt;/li&gt;
&lt;li&gt;chunk quality&lt;/li&gt;
&lt;li&gt;metadata filtering&lt;/li&gt;
&lt;li&gt;ranking quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful debugging question is:&lt;/p&gt;

&lt;p&gt;"Did the model fail, or did we give the model the wrong context?"&lt;/p&gt;

&lt;p&gt;Those are completely different problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. AI Reliability Is a System Property
&lt;/h2&gt;

&lt;p&gt;This is probably the biggest lesson I've taken from building AI applications.&lt;/p&gt;

&lt;p&gt;You cannot make an AI system reliable simply by finding a better prompt.&lt;/p&gt;

&lt;p&gt;Reliability comes from the combination of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Good Model
    +
Good Data
    +
Good Retrieval
    +
Deterministic Business Logic
    +
Validation
    +
Observability
    +
Evaluation
    +
Safe Failure Modes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is only one component.&lt;/p&gt;

&lt;p&gt;When something goes wrong, I don't immediately ask:&lt;/p&gt;

&lt;p&gt;"How do we fix the prompt?"&lt;/p&gt;

&lt;p&gt;I ask:&lt;/p&gt;

&lt;p&gt;"Which layer failed?"&lt;/p&gt;

&lt;p&gt;Was it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieval?&lt;/li&gt;
&lt;li&gt;context construction?&lt;/li&gt;
&lt;li&gt;model reasoning?&lt;/li&gt;
&lt;li&gt;tool selection?&lt;/li&gt;
&lt;li&gt;tool execution?&lt;/li&gt;
&lt;li&gt;validation?&lt;/li&gt;
&lt;li&gt;business logic?&lt;/li&gt;
&lt;li&gt;data quality?&lt;/li&gt;
&lt;li&gt;evaluation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That question usually leads to a much better solution.&lt;/p&gt;




&lt;p&gt;The Engineering Mindset I Use With AI&lt;/p&gt;

&lt;p&gt;AI development has made software engineering more interesting, not less important.&lt;/p&gt;

&lt;p&gt;The fastest prototype is rarely the hardest part.&lt;/p&gt;

&lt;p&gt;The difficult part is creating a system where you can answer:&lt;/p&gt;

&lt;p&gt;"What happens when the model is wrong?"&lt;/p&gt;

&lt;p&gt;Can we detect it?&lt;/p&gt;

&lt;p&gt;Can we measure it?&lt;/p&gt;

&lt;p&gt;Can we recover?&lt;/p&gt;

&lt;p&gt;Can we explain what happened?&lt;/p&gt;

&lt;p&gt;Can we reproduce the failure?&lt;/p&gt;

&lt;p&gt;Can we prevent the same failure from happening again?&lt;/p&gt;

&lt;p&gt;Those questions are more important to me than whether the latest model has the highest benchmark score.&lt;/p&gt;

&lt;p&gt;Python gives us an excellent foundation for building these systems.&lt;/p&gt;

&lt;p&gt;But the real advantage comes from combining AI capabilities with traditional engineering discipline:&lt;/p&gt;

&lt;p&gt;"clear interfaces, deterministic rules, strong testing, observability, evaluation, and controlled failure modes."&lt;/p&gt;

&lt;p&gt;That's the difference between an AI demo and an AI system I would trust in production.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Production AI Systems with Go: What I Learned</title>
      <dc:creator>Lee</dc:creator>
      <pubDate>Fri, 02 Oct 2026 00:03:34 +0000</pubDate>
      <link>https://dev.to/kevinpruett023_kevinpruet/building-production-ai-systems-with-go-what-i-learned-3gee</link>
      <guid>https://dev.to/kevinpruett023_kevinpruet/building-production-ai-systems-with-go-what-i-learned-3gee</guid>
      <description>&lt;p&gt;AI prototypes are easy to build.&lt;/p&gt;

&lt;p&gt;A few API calls, a prompt, and you can have something impressive running in an afternoon.&lt;/p&gt;

&lt;p&gt;Production AI is different.&lt;/p&gt;

&lt;p&gt;Once an AI feature becomes part of a real application, the engineering problems start appearing quickly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we handle concurrent requests?&lt;/li&gt;
&lt;li&gt;What happens when the model is slow?&lt;/li&gt;
&lt;li&gt;How do we control costs?&lt;/li&gt;
&lt;li&gt;How do we handle provider failures?&lt;/li&gt;
&lt;li&gt;How do we measure response quality?&lt;/li&gt;
&lt;li&gt;How do we prevent one bad request from consuming all available resources?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where I found Go particularly interesting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Go Works Well for AI Backends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI applications often spend significant time waiting on external systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   ↓
Go API
   ↓
LLM Provider
   ↓
Vector Database
   ↓
External APIs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go's concurrency model makes it natural to handle many independent operations without creating a complicated threading model.&lt;/p&gt;

&lt;p&gt;For example, an AI request might need information from multiple sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Source&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Data&lt;/span&gt;   &lt;span class="n"&gt;any&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetchContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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="nb"&gt;make&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="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fetchDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;Source&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"documents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;data&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="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;Source&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;data&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="no"&gt;nil&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;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part isn't the syntax.&lt;/p&gt;

&lt;p&gt;It's the architecture: independent operations can run concurrently while sharing cancellation through the request context.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context Cancellation Is Critical&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One mistake I've seen in AI integrations is treating model calls like ordinary function calls.&lt;/p&gt;

&lt;p&gt;They aren't.&lt;/p&gt;

&lt;p&gt;An LLM request can take seconds.&lt;/p&gt;

&lt;p&gt;If the user disconnects, there is often no reason to keep spending resources processing the request.&lt;/p&gt;

&lt;p&gt;Go's &lt;code&gt;context.Context&lt;/code&gt; gives us a clean way to propagate cancellation and deadlines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Timeouts should be intentional.&lt;/p&gt;

&lt;p&gt;An AI service without sensible timeouts can turn a temporary provider problem into a resource-exhaustion problem inside your own application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't Build Your AI System Around One Model Provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the lessons I've learned is to avoid spreading provider-specific logic throughout the application.&lt;/p&gt;

&lt;p&gt;Instead of doing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Handler
   ↓
OpenAI-specific code
   ↓
Business logic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefer something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Handler
   ↓
AI Service
   ↓
Model Interface
   ↓
Provider Adapter
&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 go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Model&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="kt"&gt;string&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&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;Now the application depends on an interface rather than a particular provider.&lt;/p&gt;

&lt;p&gt;This becomes useful when you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;switch models&lt;/li&gt;
&lt;li&gt;compare providers&lt;/li&gt;
&lt;li&gt;implement fallbacks&lt;/li&gt;
&lt;li&gt;run evaluation experiments&lt;/li&gt;
&lt;li&gt;control costs&lt;/li&gt;
&lt;li&gt;route different workloads to different models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The abstraction isn't valuable because abstractions are inherently good.&lt;/p&gt;

&lt;p&gt;It's valuable because model providers and models change quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Reliability Requires More Than HTTP Retries
&lt;/h2&gt;

&lt;p&gt;A failed API request is easy to detect.&lt;/p&gt;

&lt;p&gt;A successful API request that produces a bad answer is much harder.&lt;/p&gt;

&lt;p&gt;That's one of the biggest differences between traditional APIs and AI systems.&lt;/p&gt;

&lt;p&gt;For a traditional endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200 + valid response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;often means the operation succeeded.&lt;/p&gt;

&lt;p&gt;For an AI endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;doesn't tell you whether the answer was actually useful.&lt;/p&gt;

&lt;p&gt;You need another layer of evaluation.&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
   ↓
Model
   ↓
Response
   ↓
Validation
   ↓
Evaluation
   ↓
Accept / Retry / Fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where AI engineering starts looking less like ordinary API integration and more like building a probabilistic system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured Output Makes AI Easier to Operate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever possible, I prefer asking models for structured output rather than parsing arbitrary text.&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;"The customer appears to be interested in upgrading..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd rather receive something like:&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;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"upgrade"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&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;"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;"show_upgrade_options"&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 Go application can validate the response before acting on it.&lt;/p&gt;

&lt;p&gt;The model generates the information.&lt;/p&gt;

&lt;p&gt;The application remains responsible for enforcing the rules.&lt;/p&gt;

&lt;p&gt;That's an important boundary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep Deterministic Logic Outside the Model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LLMs are excellent at tasks involving language and ambiguity.&lt;/p&gt;

&lt;p&gt;They're not a replacement for deterministic business rules.&lt;/p&gt;

&lt;p&gt;For example, I wouldn't ask an LLM:&lt;/p&gt;

&lt;p&gt;"Is this customer allowed to receive a $500 refund?"&lt;/p&gt;

&lt;p&gt;if the answer can be determined from application data and explicit business rules.&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;LLM
 ↓
Extract intent / information
 ↓
Application
 ↓
Validate business rules
 ↓
Execute action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can help understand the request.&lt;/p&gt;

&lt;p&gt;The application should decide what the system is actually allowed to do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Observability Becomes Even More Important&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For AI applications, I want to know more than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /generate → 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful telemetry 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;model
latency
input_tokens
output_tokens
estimated_cost
status
retry_count
tool_calls
evaluation_score
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Did the new model improve quality?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Why did latency increase?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which requests are becoming expensive?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is a prompt change actually improving results?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this information, AI development becomes guesswork.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Most Important Lesson
&lt;/h2&gt;

&lt;p&gt;The biggest lesson I've learned building AI-backed systems is that the model is only one component.&lt;/p&gt;

&lt;p&gt;A production AI system looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             ┌──────────────┐
             │    Client    │
             └──────┬───────┘
                    ↓
             ┌──────────────┐
             │   Go API     │
             └──────┬───────┘
                    ↓
             ┌──────────────┐
             │ AI Service   │
             └──────┬───────┘
              ┌─────┼─────┐
              ↓     ↓     ↓
           Model  Tools  Context
              │     │     │
              └─────┼─────┘
                    ↓
             Validation
                    ↓
             Business Logic
                    ↓
              Observability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difficult part isn't calling an LLM.&lt;/p&gt;

&lt;p&gt;The difficult part is building a system around an LLM that remains "predictable, observable, cost-controlled, and safe to change".&lt;/p&gt;

&lt;p&gt;That's where Go has become particularly useful for me: it provides a relatively small, straightforward foundation for building the infrastructure around AI without making the system itself unnecessarily complicated.&lt;/p&gt;

&lt;p&gt;And in production AI, simplicity is a feature.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What Production Java Taught Me: The Problems You Don't See in Tutorials</title>
      <dc:creator>Lee</dc:creator>
      <pubDate>Fri, 02 Oct 2026 00:00:09 +0000</pubDate>
      <link>https://dev.to/kevinpruett023_kevinpruet/what-production-java-taught-me-the-problems-you-dont-see-in-tutorials-1cbg</link>
      <guid>https://dev.to/kevinpruett023_kevinpruet/what-production-java-taught-me-the-problems-you-dont-see-in-tutorials-1cbg</guid>
      <description>&lt;p&gt;Java is easy to learn.&lt;/p&gt;

&lt;p&gt;Building a Java application that remains reliable after millions of requests, growing datasets, changing requirements, and years of maintenance is a completely different problem.&lt;/p&gt;

&lt;p&gt;After working on production systems with Java, Spring Boot, REST APIs, PostgreSQL, Redis, Docker, and cloud infrastructure, I've learned that the hardest problems usually aren't about syntax or frameworks.&lt;/p&gt;

&lt;p&gt;They are about "making good engineering decisions under real constraints".&lt;/p&gt;

&lt;p&gt;Here are some lessons that have stayed with me.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't Optimize Java Before Understanding the System&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the most common mistakes I see is trying to optimize code before identifying the actual bottleneck.&lt;/p&gt;

&lt;p&gt;For example, developers may immediately focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JVM tuning&lt;/li&gt;
&lt;li&gt;object allocation&lt;/li&gt;
&lt;li&gt;stream performance&lt;/li&gt;
&lt;li&gt;garbage collection&lt;/li&gt;
&lt;li&gt;replacing loops with streams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the real problem might be a database query taking 800ms.&lt;/p&gt;

&lt;p&gt;I've seen applications where changing a single SQL query or adding the right index produced a much larger improvement than hours of Java-level optimization.&lt;/p&gt;

&lt;p&gt;My approach is usually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Measure
  ↓
Find the bottleneck
  ↓
Understand why it exists
  ↓
Fix it
  ↓
Measure again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Performance optimization without measurement is mostly guesswork.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clean Architecture Matters More as the Application Gets Older&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A small Spring Boot application can survive with almost any structure.&lt;/p&gt;

&lt;p&gt;A five-year-old application with hundreds of endpoints cannot.&lt;/p&gt;

&lt;p&gt;Over time, business logic tends to leak into controllers, repositories, utility classes, and event handlers.&lt;/p&gt;

&lt;p&gt;Eventually, changing one feature requires understanding ten unrelated parts of the system.&lt;/p&gt;

&lt;p&gt;I try to keep responsibilities clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Controller
   ↓
Application / Service Layer
   ↓
Domain Logic
   ↓
Repository / External Services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact architecture isn't the important part.&lt;/p&gt;

&lt;p&gt;The important part is knowing "where a responsibility belongs".&lt;/p&gt;

&lt;p&gt;Good architecture should make the next change easier—not simply make the current code look impressive.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Database Performance Is Part of Backend Engineering&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A Java developer working on backend systems cannot treat the database as someone else's problem.&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 sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this query runs frequently against millions of records, the right index can matter enormously.&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 sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_customer_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But indexes aren't automatically good.&lt;/p&gt;

&lt;p&gt;They increase storage requirements and can make writes more expensive.&lt;/p&gt;

&lt;p&gt;The engineering question isn't:&lt;/p&gt;

&lt;p&gt;"Should I add an index?"&lt;/p&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;p&gt;"What access pattern does this system actually need?"&lt;/p&gt;

&lt;p&gt;That distinction becomes increasingly important as traffic and data volume grow.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Distributed Systems Change Everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A method call is predictable.&lt;/p&gt;

&lt;p&gt;A network call isn't.&lt;/p&gt;

&lt;p&gt;When your Java application depends on another service, you have to assume that the dependency can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;become slow&lt;/li&gt;
&lt;li&gt;return errors&lt;/li&gt;
&lt;li&gt;time out&lt;/li&gt;
&lt;li&gt;return unexpected data&lt;/li&gt;
&lt;li&gt;become temporarily unavailable&lt;/li&gt;
&lt;li&gt;recover while your application is still processing a previous request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's why production integrations need things 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;Timeouts
Retries
Circuit breakers
Idempotency
Rate limiting
Observability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One particularly important lesson is that "retries are not always safe".&lt;/p&gt;

&lt;p&gt;Retrying a GET request is very different from blindly retrying a payment or order-creation request.&lt;/p&gt;

&lt;p&gt;Before adding retries, I ask:&lt;/p&gt;

&lt;p&gt;"What happens if the first request actually succeeded but the response was lost?"&lt;/p&gt;

&lt;p&gt;That's where idempotency becomes critical.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Concurrency Bugs Are Usually Harder Than Compilation Errors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Java gives us powerful concurrency primitives, but production concurrency problems can be extremely subtle.&lt;/p&gt;

&lt;p&gt;A race condition may happen once every few thousand requests.&lt;/p&gt;

&lt;p&gt;That makes it difficult to reproduce locally.&lt;/p&gt;

&lt;p&gt;For example, shared mutable state can create problems even when individual operations look correct.&lt;/p&gt;

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

&lt;p&gt;"Is this method thread-safe?"&lt;/p&gt;

&lt;p&gt;I prefer asking:&lt;/p&gt;

&lt;p&gt;"What state is shared, who owns it, and how can multiple requests modify it?"&lt;/p&gt;

&lt;p&gt;Reducing shared mutable state is often more valuable than simply adding synchronization everywhere.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Observability Is Part of the Feature&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Logs are not just for debugging after something breaks.&lt;/p&gt;

&lt;p&gt;A production service should help us answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happened?&lt;/li&gt;
&lt;li&gt;When did it happen?&lt;/li&gt;
&lt;li&gt;Which request caused it?&lt;/li&gt;
&lt;li&gt;Which dependency failed?&lt;/li&gt;
&lt;li&gt;How long did it take?&lt;/li&gt;
&lt;li&gt;How many users were affected?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For important operations, structured logging and metrics are incredibly valuable.&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
customer_id
operation
duration_ms
status
dependency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When something goes wrong at 2 AM, observability can be the difference between a five-minute investigation and a two-hour investigation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't Hide Business Logic Behind Frameworks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Spring is powerful.&lt;/p&gt;

&lt;p&gt;But Spring shouldn't become the architecture.&lt;/p&gt;

&lt;p&gt;I've seen codebases where developers rely so heavily on annotations and framework behavior that it becomes difficult to understand what the application actually does.&lt;/p&gt;

&lt;p&gt;Frameworks should support business logic.&lt;/p&gt;

&lt;p&gt;They shouldn't define it.&lt;/p&gt;

&lt;p&gt;A good test is:&lt;/p&gt;

&lt;p&gt;If I removed the framework annotations, could I still explain the important business rules?&lt;/p&gt;

&lt;p&gt;If the answer is no, there may be too much framework coupling.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tests Should Protect Behavior, Not Implementation Details&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I don't believe every line needs a unit test.&lt;/p&gt;

&lt;p&gt;I care more about whether the important behavior is protected.&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;Given an expired subscription
When the customer requests a premium operation
Then the operation must be rejected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a business rule.&lt;/p&gt;

&lt;p&gt;A good test protects that rule even if the internal implementation changes.&lt;/p&gt;

&lt;p&gt;This also makes refactoring much safer.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Simplicity Is an Engineering Skill&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Senior engineers aren't valuable because they can write complicated code.&lt;/p&gt;

&lt;p&gt;They're valuable because they can recognize when complexity isn't necessary.&lt;/p&gt;

&lt;p&gt;I've learned to question things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do we really need another service?&lt;/li&gt;
&lt;li&gt;Do we need asynchronous processing here?&lt;/li&gt;
&lt;li&gt;Does this need a distributed cache?&lt;/li&gt;
&lt;li&gt;Do we need a custom framework?&lt;/li&gt;
&lt;li&gt;Is this abstraction solving a real problem?&lt;/li&gt;
&lt;li&gt;Will the next developer understand this six months from now?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes the best architecture is simply the boring one.&lt;/p&gt;

&lt;p&gt;And boring systems are often the systems that are easiest to operate.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;After years of working with Java, my biggest lesson isn't about Java itself.&lt;/p&gt;

&lt;p&gt;It's this:&lt;/p&gt;

&lt;p&gt;"Production engineering is mostly about managing complexity."&lt;/p&gt;

&lt;p&gt;Java gives us excellent tools for building large systems, but the language and framework can't make architectural decisions for us.&lt;/p&gt;

&lt;p&gt;The real skill is knowing when to optimize, when to simplify, when to introduce abstraction, when to accept complexity, and—perhaps most importantly—when "not" to add another layer.&lt;/p&gt;

&lt;p&gt;The best production code I've worked on isn't necessarily the most clever code.&lt;/p&gt;

&lt;p&gt;It's the code that another engineer can understand, monitor, test, change, and safely deploy years later.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why Good Engineers Solve Problems Before They Write Code</title>
      <dc:creator>Lee</dc:creator>
      <pubDate>Tue, 29 Sep 2026 00:52:48 +0000</pubDate>
      <link>https://dev.to/kevinpruett023_kevinpruet/why-good-engineers-solve-problems-before-they-write-code-25fc</link>
      <guid>https://dev.to/kevinpruett023_kevinpruet/why-good-engineers-solve-problems-before-they-write-code-25fc</guid>
      <description>&lt;p&gt;A common misunderstanding in software development is that engineers are paid to write code.&lt;/p&gt;

&lt;p&gt;Of course, coding is part of the job.&lt;/p&gt;

&lt;p&gt;But the most valuable engineering work often happens before the first line of code is written.&lt;/p&gt;

&lt;p&gt;The best engineers spend time understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem are we actually solving?&lt;/li&gt;
&lt;li&gt;Who experiences this problem?&lt;/li&gt;
&lt;li&gt;What constraints exist?&lt;/li&gt;
&lt;li&gt;What can fail?&lt;/li&gt;
&lt;li&gt;How will this system evolve?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because a perfectly written solution to the wrong problem is still a failure.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Requirements Are Usually Symptoms, Not Problems
&lt;/h1&gt;

&lt;p&gt;A product manager says:&lt;/p&gt;

&lt;p&gt;"We need a dashboard."&lt;/p&gt;

&lt;p&gt;A developer starts creating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React components&lt;/li&gt;
&lt;li&gt;charts&lt;/li&gt;
&lt;li&gt;API endpoints&lt;/li&gt;
&lt;li&gt;database tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But a senior engineer asks:&lt;/p&gt;

&lt;p&gt;"Why does the business need this dashboard?"&lt;/p&gt;

&lt;p&gt;Maybe the real problem is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;managers cannot understand customer behavior&lt;/li&gt;
&lt;li&gt;teams spend hours creating reports manually&lt;/li&gt;
&lt;li&gt;important metrics are hidden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dashboard is only one possible solution.&lt;/p&gt;

&lt;p&gt;Understanding the actual problem creates better software.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Every Technical Decision Has a Cost
&lt;/h1&gt;

&lt;p&gt;Technology decisions are not only about what works today.&lt;/p&gt;

&lt;p&gt;They create future responsibilities.&lt;/p&gt;

&lt;p&gt;Choosing a framework means considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;learning curve&lt;/li&gt;
&lt;li&gt;maintenance cost&lt;/li&gt;
&lt;li&gt;available talent&lt;/li&gt;
&lt;li&gt;ecosystem stability&lt;/li&gt;
&lt;li&gt;long-term support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing a database means considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data relationships&lt;/li&gt;
&lt;li&gt;transaction requirements&lt;/li&gt;
&lt;li&gt;scaling patterns&lt;/li&gt;
&lt;li&gt;operational complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A senior engineer does not ask:&lt;/p&gt;

&lt;p&gt;"Can we build this?"&lt;/p&gt;

&lt;p&gt;They ask:&lt;/p&gt;

&lt;p&gt;"What will this decision cost us later?"&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Simple Systems Are Usually More Difficult to Design
&lt;/h1&gt;

&lt;p&gt;Complex systems are easy to create.&lt;/p&gt;

&lt;p&gt;You can always add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more services&lt;/li&gt;
&lt;li&gt;more libraries&lt;/li&gt;
&lt;li&gt;more infrastructure&lt;/li&gt;
&lt;li&gt;more abstraction layers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difficult question is:&lt;/p&gt;

&lt;p&gt;"What can we remove?"&lt;/p&gt;

&lt;p&gt;Great engineers constantly search for simplicity.&lt;/p&gt;

&lt;p&gt;A simple architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is easier to understand&lt;/li&gt;
&lt;li&gt;is easier to debug&lt;/li&gt;
&lt;li&gt;reduces operational risk&lt;/li&gt;
&lt;li&gt;allows faster development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simplicity is not the absence of engineering.&lt;/p&gt;

&lt;p&gt;Simplicity is the result of good engineering.&lt;/p&gt;

&lt;h1&gt;
  
  
  4. The First Version Should Be Designed for Learning
&lt;/h1&gt;

&lt;p&gt;Many teams try to build the perfect product immediately.&lt;/p&gt;

&lt;p&gt;They spend months designing features that users may never need.&lt;/p&gt;

&lt;p&gt;A better approach:&lt;/p&gt;

&lt;p&gt;Build a system that helps you learn.&lt;/p&gt;

&lt;p&gt;The first version should answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do users need this?&lt;/li&gt;
&lt;li&gt;Does the workflow make sense?&lt;/li&gt;
&lt;li&gt;What problems appear in real usage?&lt;/li&gt;
&lt;li&gt;Which features create value?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Software development is not only construction.&lt;/p&gt;

&lt;p&gt;It is continuous discovery.&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Reliability Comes From Thinking About Failure
&lt;/h1&gt;

&lt;p&gt;Most developers design for success:&lt;/p&gt;

&lt;p&gt;"User clicks the button and everything works."&lt;/p&gt;

&lt;p&gt;Production systems need another mindset:&lt;/p&gt;

&lt;p&gt;"What happens when something fails?"&lt;/p&gt;

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

&lt;p&gt;Payment service unavailable.&lt;/p&gt;

&lt;p&gt;Questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can the user retry?&lt;/li&gt;
&lt;li&gt;Is the payment duplicated?&lt;/li&gt;
&lt;li&gt;Is the transaction recorded?&lt;/li&gt;
&lt;li&gt;Is someone notified?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;External API timeout.&lt;/p&gt;

&lt;p&gt;Questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do we have a fallback?&lt;/li&gt;
&lt;li&gt;Should we retry?&lt;/li&gt;
&lt;li&gt;How many times?&lt;/li&gt;
&lt;li&gt;How do we alert the team?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A mature system is not one that never fails.&lt;/p&gt;

&lt;p&gt;A mature system is one that fails safely.&lt;/p&gt;

&lt;h1&gt;
  
  
  6. AI Makes Implementation Faster, But Thinking More Important
&lt;/h1&gt;

&lt;p&gt;Modern AI tools can help engineers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate code&lt;/li&gt;
&lt;li&gt;explain unfamiliar systems&lt;/li&gt;
&lt;li&gt;create tests&lt;/li&gt;
&lt;li&gt;automate repetitive work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But faster implementation creates a new challenge:&lt;/p&gt;

&lt;p&gt;More code can be produced faster than it can be understood.&lt;/p&gt;

&lt;p&gt;The engineer's responsibility becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validating assumptions&lt;/li&gt;
&lt;li&gt;reviewing architecture&lt;/li&gt;
&lt;li&gt;identifying risks&lt;/li&gt;
&lt;li&gt;protecting quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The future belongs to engineers who can combine:&lt;/p&gt;

&lt;p&gt;technical speed + engineering judgment.&lt;/p&gt;

&lt;h1&gt;
  
  
  7. Senior Engineers Create Confidence
&lt;/h1&gt;

&lt;p&gt;A strong engineer does more than complete tickets.&lt;/p&gt;

&lt;p&gt;They create confidence.&lt;/p&gt;

&lt;p&gt;Confidence that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the system can handle growth&lt;/li&gt;
&lt;li&gt;changes will not break everything&lt;/li&gt;
&lt;li&gt;security risks are considered&lt;/li&gt;
&lt;li&gt;failures can be recovered&lt;/li&gt;
&lt;li&gt;the team understands the architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Companies do not only need people who can build features.&lt;/p&gt;

&lt;p&gt;They need people who can build reliable foundations.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Writing code is a technical skill.&lt;/p&gt;

&lt;p&gt;Building software is an engineering discipline.&lt;/p&gt;

&lt;p&gt;The difference is the ability to see beyond the current task and understand the entire system:&lt;/p&gt;

&lt;p&gt;the users,&lt;br&gt;
the business,&lt;br&gt;
the technology,&lt;br&gt;
and the future.&lt;/p&gt;

&lt;p&gt;The best engineers do not just create software.&lt;/p&gt;

&lt;p&gt;They create solutions that continue working when reality becomes complicated.&lt;/p&gt;

&lt;p&gt;Let's Connect&lt;/p&gt;

&lt;p&gt;I enjoy discussing and collaborating on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full-stack application architecture&lt;/li&gt;
&lt;li&gt;Backend engineering&lt;/li&gt;
&lt;li&gt;Cloud infrastructure&lt;/li&gt;
&lt;li&gt;AI-powered products&lt;/li&gt;
&lt;li&gt;System design challenges&lt;/li&gt;
&lt;li&gt;Building reliable software teams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building something interesting or solving a difficult technical problem, feel free to connect.&lt;/p&gt;

&lt;p&gt;Great products are created when engineers, founders, and teams share ideas and build together.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Hidden Engineering Behind Scalable Applications: What Users Never See</title>
      <dc:creator>Lee</dc:creator>
      <pubDate>Mon, 28 Sep 2026 16:15:08 +0000</pubDate>
      <link>https://dev.to/kevinpruett023_kevinpruet/the-hidden-engineering-behind-scalable-applications-what-users-never-see-ddl</link>
      <guid>https://dev.to/kevinpruett023_kevinpruet/the-hidden-engineering-behind-scalable-applications-what-users-never-see-ddl</guid>
      <description>&lt;p&gt;When users open an application, they only see the final result.&lt;/p&gt;

&lt;p&gt;A beautiful interface.&lt;br&gt;
A fast response.&lt;br&gt;
A smooth experience.&lt;/p&gt;

&lt;p&gt;They do not see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;thousands of database queries&lt;/li&gt;
&lt;li&gt;background workers processing tasks&lt;/li&gt;
&lt;li&gt;authentication systems protecting accounts&lt;/li&gt;
&lt;li&gt;monitoring systems detecting failures&lt;/li&gt;
&lt;li&gt;infrastructure automatically recovering from problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best engineering is often invisible.&lt;/p&gt;

&lt;p&gt;A great application feels simple because engineers worked hard to hide the complexity.&lt;/p&gt;



&lt;ol&gt;
&lt;li&gt;A Feature Is Easy. A Reliable Feature Is Hard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Anyone can build a login page.&lt;/p&gt;

&lt;p&gt;The real engineering begins after the first 10,000 users.&lt;/p&gt;

&lt;p&gt;A production authentication system must answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when someone enters the wrong password 10 times?&lt;/li&gt;
&lt;li&gt;How are sessions managed?&lt;/li&gt;
&lt;li&gt;How do we revoke access?&lt;/li&gt;
&lt;li&gt;How do we handle expired tokens?&lt;/li&gt;
&lt;li&gt;How do we prevent account takeover?&lt;/li&gt;
&lt;li&gt;How do we monitor suspicious behavior?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The visible feature is:&lt;/p&gt;

&lt;p&gt;"User can log in."&lt;/p&gt;

&lt;p&gt;The invisible system is:&lt;/p&gt;

&lt;p&gt;"Millions of users can safely access their accounts under unpredictable conditions."&lt;/p&gt;

&lt;p&gt;That difference defines production engineering.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Databases Are Where Applications Become Fast or Slow&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Many applications start simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend
   |
Backend API
   |
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then growth happens.&lt;/p&gt;

&lt;p&gt;Suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pages become slower&lt;/li&gt;
&lt;li&gt;reports timeout&lt;/li&gt;
&lt;li&gt;APIs consume more resources&lt;/li&gt;
&lt;li&gt;users experience random delays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is usually not the database itself.&lt;/p&gt;

&lt;p&gt;The problem is how the application communicates with it.&lt;/p&gt;

&lt;p&gt;Senior engineers think about:&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Modeling
&lt;/h2&gt;

&lt;p&gt;A good database design prevents future problems.&lt;/p&gt;

&lt;p&gt;Questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should this data be normalized?&lt;/li&gt;
&lt;li&gt;What relationships exist?&lt;/li&gt;
&lt;li&gt;What information changes frequently?&lt;/li&gt;
&lt;li&gt;What should be cached?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Query Efficiency
&lt;/h2&gt;

&lt;p&gt;A query that works with 1,000 records may fail with 10 million.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;proper indexing&lt;/li&gt;
&lt;li&gt;query analysis&lt;/li&gt;
&lt;li&gt;avoiding unnecessary joins&lt;/li&gt;
&lt;li&gt;pagination&lt;/li&gt;
&lt;li&gt;caching frequently accessed data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance starts with design, not optimization tools.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scalability Is More Than Adding Servers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A common misunderstanding:&lt;/p&gt;

&lt;p&gt;"More users means more servers."&lt;/p&gt;

&lt;p&gt;Sometimes yes.&lt;/p&gt;

&lt;p&gt;But adding servers to a poorly designed system only creates a more expensive problem.&lt;/p&gt;

&lt;p&gt;A scalable system considers:&lt;/p&gt;

&lt;h2&gt;
  
  
  Stateless Services
&lt;/h2&gt;

&lt;p&gt;Applications should avoid storing important user state inside a single server.&lt;/p&gt;

&lt;p&gt;This allows:&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

      |
      |
Load Balancer

   /    |    \

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

&lt;/div&gt;



&lt;p&gt;Any server can handle the request.&lt;/p&gt;




&lt;h2&gt;
  
  
  Background Processing
&lt;/h2&gt;

&lt;p&gt;Not every task should happen immediately.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;A user uploads a video.&lt;/p&gt;

&lt;p&gt;Bad design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upload
 |
Process video
 |
Generate thumbnail
 |
Send notification
 |
Return response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user waits.&lt;/p&gt;

&lt;p&gt;Better design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upload
 |
Store file
 |
Queue task
 |
Return response

Background workers:
- Process video
- Generate thumbnail
- Send notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application feels instant.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Observability: Knowing What Your System Is Doing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A system without monitoring is a system you cannot control.&lt;/p&gt;

&lt;p&gt;Production applications need:&lt;/p&gt;

&lt;h2&gt;
  
  
  Logs
&lt;/h2&gt;

&lt;p&gt;"What happened?"&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Payment request failed:
User ID: 4521
Transaction ID: 89321
Error: Timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Metrics
&lt;/h2&gt;

&lt;p&gt;"How often is it happening?"&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;API response time&lt;/li&gt;
&lt;li&gt;Error rate&lt;/li&gt;
&lt;li&gt;CPU usage&lt;/li&gt;
&lt;li&gt;Database latency&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tracing
&lt;/h2&gt;

&lt;p&gt;"Where did the problem happen?"&lt;/p&gt;

&lt;p&gt;A request may travel through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend
 |
API Gateway
 |
Authentication Service
 |
Payment Service
 |
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tracing shows exactly where the delay occurred.&lt;/p&gt;




&lt;h1&gt;
  
  
  5. Clean Code Is Not About Style
&lt;/h1&gt;

&lt;p&gt;Many developers think clean code means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;beautiful formatting&lt;/li&gt;
&lt;li&gt;short functions&lt;/li&gt;
&lt;li&gt;perfect naming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those matter.&lt;/p&gt;

&lt;p&gt;But the deeper meaning is:&lt;/p&gt;

&lt;p&gt;Clean code reduces future decision-making cost.&lt;/p&gt;

&lt;p&gt;Imagine joining a project after two years.&lt;/p&gt;

&lt;p&gt;You need to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why was this architecture chosen?&lt;/li&gt;
&lt;li&gt;Why does this service exist?&lt;/li&gt;
&lt;li&gt;Why is this database structure like this?&lt;/li&gt;
&lt;li&gt;What happens if this function changes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good code communicates the reasoning behind decisions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Role of AI in Modern Engineering&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AI tools are changing development workflows.&lt;/p&gt;

&lt;p&gt;Today, engineers can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate prototypes quickly&lt;/li&gt;
&lt;li&gt;analyze large codebases&lt;/li&gt;
&lt;li&gt;automate repetitive tasks&lt;/li&gt;
&lt;li&gt;create documentation faster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the responsibility increases.&lt;/p&gt;

&lt;p&gt;A senior engineer must evaluate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is generated code secure?&lt;/li&gt;
&lt;li&gt;Is the architecture correct?&lt;/li&gt;
&lt;li&gt;Does it follow business requirements?&lt;/li&gt;
&lt;li&gt;Can the team maintain it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The future engineer is not someone who writes the most code.&lt;/p&gt;

&lt;p&gt;It is someone who can direct technology toward meaningful outcomes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Building Systems That Last&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A successful application is not measured only by launch day.&lt;/p&gt;

&lt;p&gt;Real success appears months and years later.&lt;/p&gt;

&lt;p&gt;When:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;new developers can understand the code&lt;/li&gt;
&lt;li&gt;users trust the platform&lt;/li&gt;
&lt;li&gt;features can be added safely&lt;/li&gt;
&lt;li&gt;failures can be recovered quickly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is when engineering creates lasting value.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Software engineering is the art of managing complexity.&lt;/p&gt;

&lt;p&gt;The user sees simplicity.&lt;/p&gt;

&lt;p&gt;The engineer creates the invisible foundation that makes that simplicity possible.&lt;/p&gt;

&lt;p&gt;Every fast application, every reliable platform, and every successful product is built on thousands of thoughtful decisions.&lt;/p&gt;

&lt;p&gt;That is the difference between writing software and engineering systems.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Difference Between Writing Code and Building Software Systems</title>
      <dc:creator>Lee</dc:creator>
      <pubDate>Mon, 28 Sep 2026 16:04:22 +0000</pubDate>
      <link>https://dev.to/kevinpruett023_kevinpruet/the-difference-between-writing-code-and-building-software-systems-59o8</link>
      <guid>https://dev.to/kevinpruett023_kevinpruet/the-difference-between-writing-code-and-building-software-systems-59o8</guid>
      <description>&lt;p&gt;After years of building web applications, APIs, distributed systems, and AI-powered products, I have learned one important lesson:&lt;/p&gt;

&lt;p&gt;Senior engineering is not about writing more code. It is about making better decisions before writing code.&lt;/p&gt;

&lt;p&gt;A junior developer often asks:&lt;/p&gt;

&lt;p&gt;"How can I implement this feature?"&lt;/p&gt;

&lt;p&gt;A senior engineer asks:&lt;/p&gt;

&lt;p&gt;"Why does this feature exist? What problem does it solve? How will it behave under failure? How will it evolve six months from now?"&lt;/p&gt;

&lt;p&gt;The difference is not syntax. It is perspective.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Software Is Not a Collection of Features&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Many applications fail not because individual features are badly implemented, but because the system was designed without considering the bigger picture.&lt;/p&gt;

&lt;p&gt;A simple requirement:&lt;/p&gt;

&lt;p&gt;"Users should be able to upload files."&lt;/p&gt;

&lt;p&gt;Sounds easy.&lt;/p&gt;

&lt;p&gt;But an experienced engineer immediately thinks about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How large can files become?&lt;/li&gt;
&lt;li&gt;Should uploads block API requests?&lt;/li&gt;
&lt;li&gt;Where should files be stored?&lt;/li&gt;
&lt;li&gt;How do we scan malicious content?&lt;/li&gt;
&lt;li&gt;What happens if storage fails?&lt;/li&gt;
&lt;li&gt;How do we retry failed uploads?&lt;/li&gt;
&lt;li&gt;How do we monitor upload performance?&lt;/li&gt;
&lt;li&gt;How do we handle millions of files?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The feature is small.&lt;/p&gt;

&lt;p&gt;The system behind it is not.&lt;/p&gt;

&lt;p&gt;Good engineering means understanding the hidden complexity behind simple requests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Architecture Is About Managing Future Change&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The best architecture is not the one with the most technologies.&lt;/p&gt;

&lt;p&gt;It is the one that allows change without creating chaos.&lt;/p&gt;

&lt;p&gt;I have seen many projects where teams immediately introduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microservices&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Event-driven architecture&lt;/li&gt;
&lt;li&gt;Multiple databases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;before understanding the actual problem.&lt;/p&gt;

&lt;p&gt;Complexity is not the same as scalability.&lt;/p&gt;

&lt;p&gt;Sometimes a well-designed monolith with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear boundaries&lt;/li&gt;
&lt;li&gt;modular services&lt;/li&gt;
&lt;li&gt;proper database design&lt;/li&gt;
&lt;li&gt;good testing&lt;/li&gt;
&lt;li&gt;observability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;can outperform a poorly designed distributed system.&lt;/p&gt;

&lt;p&gt;The goal is not to build the most impressive architecture.&lt;/p&gt;

&lt;p&gt;The goal is to build the architecture that creates business value.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Performance Problems Are Usually Design Problems&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When an application becomes slow, many developers immediately look for faster hardware.&lt;/p&gt;

&lt;p&gt;But most performance problems come from decisions made earlier.&lt;/p&gt;

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

&lt;p&gt;A slow API:&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
      |
      |
Controller
      |
      |
Database
      |
      |
500 unnecessary queries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution is not always adding servers.&lt;/p&gt;

&lt;p&gt;Sometimes it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better indexing&lt;/li&gt;
&lt;li&gt;query optimization&lt;/li&gt;
&lt;li&gt;caching strategy&lt;/li&gt;
&lt;li&gt;pagination&lt;/li&gt;
&lt;li&gt;asynchronous processing&lt;/li&gt;
&lt;li&gt;reducing unnecessary data transfer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A senior engineer learns to find the root cause instead of treating symptoms.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Security Is Not a Final Step&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Security should not be a checklist before deployment.&lt;/p&gt;

&lt;p&gt;Security is part of design.&lt;/p&gt;

&lt;p&gt;Every system should answer:&lt;/p&gt;

&lt;p&gt;Authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who are you?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authorization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are you allowed to do?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Validation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What input can the system trust?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Data protection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What information should never be exposed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Operational security:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we detect attacks?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A secure application is not created by adding a security library.&lt;/p&gt;

&lt;p&gt;It is created through thousands of small engineering decisions.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;AI Is Changing Software Engineering, But Fundamentals Matter More&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Today, developers can generate code faster than ever.&lt;/p&gt;

&lt;p&gt;AI can create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API endpoints&lt;/li&gt;
&lt;li&gt;UI components&lt;/li&gt;
&lt;li&gt;database models&lt;/li&gt;
&lt;li&gt;documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But generating code is not the same as engineering.&lt;/p&gt;

&lt;p&gt;The difficult questions remain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this design correct?&lt;/li&gt;
&lt;li&gt;Is this secure?&lt;/li&gt;
&lt;li&gt;Will it scale?&lt;/li&gt;
&lt;li&gt;Is the data model appropriate?&lt;/li&gt;
&lt;li&gt;Does this solve the actual problem?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI increases developer speed.&lt;/p&gt;

&lt;p&gt;Engineering judgment determines direction.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;The Senior Engineer Mindset&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A senior engineer is not someone who knows every framework.&lt;/p&gt;

&lt;p&gt;Technology changes too quickly.&lt;/p&gt;

&lt;p&gt;A senior engineer understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;principles over tools&lt;/li&gt;
&lt;li&gt;systems over files&lt;/li&gt;
&lt;li&gt;outcomes over tasks&lt;/li&gt;
&lt;li&gt;reliability over quick fixes&lt;/li&gt;
&lt;li&gt;simplicity over unnecessary complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frameworks come and go.&lt;/p&gt;

&lt;p&gt;Good engineering thinking remains.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Building Software That People Can Trust&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most valuable applications are not necessarily the ones with the most features.&lt;/p&gt;

&lt;p&gt;They are the ones users can depend on.&lt;/p&gt;

&lt;p&gt;They:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;respond quickly&lt;/li&gt;
&lt;li&gt;protect user data&lt;/li&gt;
&lt;li&gt;recover from failures&lt;/li&gt;
&lt;li&gt;scale with growth&lt;/li&gt;
&lt;li&gt;remain understandable years later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing code is creating instructions for computers.&lt;/p&gt;

&lt;p&gt;Building software is creating a system that humans can trust.&lt;/p&gt;

&lt;p&gt;That is the difference.&lt;/p&gt;

&lt;p&gt;Let's Connect&lt;/p&gt;

&lt;p&gt;I enjoy discussing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full-stack architecture&lt;/li&gt;
&lt;li&gt;Backend scalability&lt;/li&gt;
&lt;li&gt;Cloud-native systems&lt;/li&gt;
&lt;li&gt;AI engineering and automation&lt;/li&gt;
&lt;li&gt;Developer productivity&lt;/li&gt;
&lt;li&gt;Building reliable products from ideas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are working on an interesting product, solving a challenging engineering problem, or exploring collaboration opportunities, feel free to connect.&lt;/p&gt;

&lt;p&gt;Always open to exchanging ideas with engineers, founders, and builders around the world.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>software</category>
      <category>softwareengineering</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
