<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Bill LIao's Blog]]></title><description><![CDATA[A technical blog on modern backend development, software architecture, and practical AI agent workflows]]></description><link>https://billliao.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Bill LIao&apos;s Blog</title><link>https://billliao.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 16 Sep 2026 17:51:01 GMT</lastBuildDate><atom:link href="https://billliao.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building Your First AI Agent: A Hands-On Guide from Prototype to Production]]></title><description><![CDATA[What if your first AI agent doesn’t fail because the LLM is bad—but because you treated an agent like a chatbot?
Building an AI agent is surprisingly easy.
Building one that can be trusted in producti]]></description><link>https://billliao.hashnode.dev/building-your-first-ai-agent-a-hands-on-guide-from-prototype-to-production</link><guid isPermaLink="true">https://billliao.hashnode.dev/building-your-first-ai-agent-a-hands-on-guide-from-prototype-to-production</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:12:10 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/131f3a3d-cca9-4e4c-9be1-0f9bfab904aa.png" alt="" style="display:block;margin:0 auto" />

<p><strong>What if your first AI agent doesn’t fail because the LLM is bad—but because you treated an agent like a chatbot?</strong></p>
<p>Building an AI agent is surprisingly easy.</p>
<p>Building one that can be trusted in production is a completely different engineering problem.</p>
<p>A prototype can be built in an afternoon.</p>
<p>A production-ready agent requires architecture, tools, state management, security, observability, evaluation, failure handling, and operational discipline.</p>
<p>So how do you get from:</p>
<p><strong>“It works in a demo.”</strong></p>
<p>to:</p>
<p><strong>“We can safely run this in production.”</strong></p>
<p>Here is a practical journey.</p>
<hr />
<h3>1. Start With the Problem, Not the Model</h3>
<p>The biggest mistake when building your first AI agent is starting with:</p>
<blockquote>
<p>“Which LLM should I use?”</p>
</blockquote>
<p>Start with:</p>
<blockquote>
<p><strong>“What decision or workflow should the agent own?”</strong></p>
</blockquote>
<p>A good first agent should have:</p>
<ul>
<li><p>A clearly defined objective</p>
</li>
<li><p>A limited set of actions</p>
</li>
<li><p>Well-defined inputs and outputs</p>
</li>
<li><p>A measurable success criterion</p>
</li>
<li><p>A reasonable failure mode</p>
</li>
</ul>
<p>For example:</p>
<p>Instead of building a generic “Customer Support Agent,” start with:</p>
<blockquote>
<p><strong>“Analyze a customer request, retrieve relevant account information, determine the appropriate category, and create a support ticket.”</strong></p>
</blockquote>
<p>That is something you can actually test.</p>
<hr />
<h2>2. Understand the Difference Between a Chatbot and an Agent</h2>
<p>A chatbot primarily generates responses.</p>
<p>An agent can:</p>
<p><strong>Observe → Reason → Act → Observe → Adapt → Act</strong></p>
<p>A simplified architecture looks like this:</p>
<pre><code class="language-plaintext"> ┌──────────────┐
                    │     User     │
                    └──────┬───────┘
                           │
                           ▼
                    ┌──────────────┐
                    │     Agent    │
                    │     Brain    │
                    └──────┬───────┘
                           │
             ┌─────────────┼─────────────┐
             ▼             ▼             ▼
        ┌─────────┐   ┌─────────┐   ┌─────────┐
        │  Tools  │   │ Memory  │   │  RAG    │
        └─────────┘   └─────────┘   └─────────┘
             │             │             │
             ▼             ▼             ▼
        APIs / DBs     State        Knowledge
</code></pre>
<p>The LLM is only one component.</p>
<p>The <strong>agent system</strong> is the combination of:</p>
<p><strong>Model + Context + Tools + State + Control Logic + Guardrails</strong></p>
<p>This distinction becomes critical when moving toward production.</p>
<hr />
<h2>3. Build the Smallest Possible Prototype</h2>
<p>Don't start with a multi-agent architecture.</p>
<p>Don't build 20 tools.</p>
<p>Don't create a complex memory system.</p>
<p>Build the smallest loop that proves the idea.</p>
<p>For example:</p>
<pre><code class="language-plaintext">User Request
     ↓
Understand Intent
     ↓
Select Tool
     ↓
Execute Tool
     ↓
Analyze Result
     ↓
Generate Response
</code></pre>
<p>Suppose we're building an internal IT agent.</p>
<p>The first version might have only three tools:</p>
<pre><code class="language-plaintext">get_user()
search_knowledge_base()
create_ticket()
</code></pre>
<p>That's enough to demonstrate whether the concept actually works.</p>
<hr />
<h2>4. Give the Agent Tools</h2>
<p>Tools are what transform an LLM from a conversational system into an operational system.</p>
<p>Examples:</p>
<pre><code class="language-plaintext">Database
REST API
GraphQL API
Search Engine
File System
Git Repository
Cloud Service
Business Workflow
MCP Server
</code></pre>
<p>A tool should have:</p>
<ul>
<li><p>A clear name</p>
</li>
<li><p>A precise description</p>
</li>
<li><p>Strong input validation</p>
</li>
<li><p>A predictable output format</p>
</li>
<li><p>Explicit authorization rules</p>
</li>
</ul>
<p>For example:</p>
<pre><code class="language-plaintext">{
  "name": "create_ticket",
  "description": "Create a support ticket for an authenticated customer",
  "parameters": {
    "customerId": "string",
    "priority": "enum",
    "description": "string"
  }
}
</code></pre>
<p>The key principle:</p>
<blockquote>
<p><strong>Never give an agent more capabilities than it actually needs.</strong></p>
</blockquote>
<p>The blast radius of an agent grows with its permissions.</p>
<hr />
<h2>5. Add Retrieval When Knowledge Matters</h2>
<p>LLMs already know a lot.</p>
<p>But they don't know your company's latest:</p>
<ul>
<li><p>Policies</p>
</li>
<li><p>Customer data</p>
</li>
<li><p>Product documentation</p>
</li>
<li><p>Internal architecture</p>
</li>
<li><p>Operational procedures</p>
</li>
<li><p>Private knowledge</p>
</li>
</ul>
<p>That's where RAG becomes useful.</p>
<p>A typical architecture is:</p>
<pre><code class="language-plaintext"> User Query
                     │
                     ▼
              Query Understanding
                     │
                     ▼
              Vector / Hybrid Search
                     │
                     ▼
              Relevant Documents
                     │
                     ▼
                  Context
                     │
                     ▼
                    LLM
                     │
                     ▼
                  Response
</code></pre>
<p>But don't automatically add RAG.</p>
<p>Ask first:</p>
<blockquote>
<p><strong>Does the agent actually need external knowledge to complete the task?</strong></p>
</blockquote>
<p>If the answer is no, RAG may simply add complexity and latency.</p>
<hr />
<h2>6. Introduce Memory Carefully</h2>
<p>Memory is another area where prototypes often become unnecessarily complicated.</p>
<p>There are at least three different concepts:</p>
<h3>Short-term memory</h3>
<p>Conversation or workflow state.</p>
<pre><code class="language-plaintext">User → Agent → Tool → Result → Agent
</code></pre>
<h3>Long-term memory</h3>
<p>Persistent information about previous interactions.</p>
<pre><code class="language-plaintext">User Preferences
Previous Decisions
Historical Context
</code></pre>
<h3>Business state</h3>
<p>Information that belongs in your transactional systems.</p>
<pre><code class="language-plaintext">Customer Status
Order State
Account Balance
Workflow Status
</code></pre>
<p>A critical architectural rule:</p>
<blockquote>
<p><strong>Don't use an LLM memory store as a replacement for your system of record.</strong></p>
</blockquote>
<p>Your CRM should remain your CRM.</p>
<p>Your database should remain your database.</p>
<p>Agent memory should provide context—not become an uncontrolled source of truth.</p>
<hr />
<h2>7. Add Guardrails Before Giving the Agent More Power</h2>
<p>A prototype can safely experiment.</p>
<p>Production cannot.</p>
<p>Consider an agent that can:</p>
<pre><code class="language-plaintext">Read customer data
Create tickets
Send emails
Modify records
Trigger payments
Deploy code
</code></pre>
<p>The question isn't:</p>
<blockquote>
<p>“Can the LLM perform these actions?”</p>
</blockquote>
<p>The question is:</p>
<blockquote>
<p><strong>“Under what conditions should it be allowed to perform them?”</strong></p>
</blockquote>
<p>Introduce guardrails such as:</p>
<pre><code class="language-plaintext">Authentication
Authorization
Input Validation
Output Validation
Rate Limiting
Tool Permissions
Human Approval
Data Access Policies
Audit Logging
</code></pre>
<p>For high-impact operations:</p>
<pre><code class="language-plaintext">Agent
  ↓
Risk Assessment
  ↓
Human Approval
  ↓
Tool Execution
</code></pre>
<p>Human-in-the-loop isn't necessarily a failure of autonomy.</p>
<p>It can be a deliberate <strong>risk-control mechanism</strong>.</p>
<hr />
<h2>8. Treat the Agent as a Distributed System</h2>
<p>This is where AI engineering starts looking much more familiar to experienced software engineers.</p>
<p>An agent can have:</p>
<ul>
<li><p>Network calls</p>
</li>
<li><p>External APIs</p>
</li>
<li><p>Databases</p>
</li>
<li><p>Queues</p>
</li>
<li><p>Model inference</p>
</li>
<li><p>Long-running workflows</p>
</li>
<li><p>Partial failures</p>
</li>
<li><p>Timeouts</p>
</li>
<li><p>Retries</p>
</li>
<li><p>Duplicate operations</p>
</li>
<li><p>Race conditions</p>
</li>
</ul>
<p>In other words:</p>
<blockquote>
<p><strong>Your AI agent is a distributed system with probabilistic decision-making.</strong></p>
</blockquote>
<p>That changes how you design it.</p>
<p>You need:</p>
<h3>Timeouts</h3>
<p>Never assume a tool will respond.</p>
<h3>Retries</h3>
<p>Retry transient failures—but carefully.</p>
<h3>Idempotency</h3>
<p>A repeated tool call shouldn't accidentally create two payments or two customer records.</p>
<h3>Circuit breakers</h3>
<p>Don't allow a failing downstream service to bring down the entire agent workflow.</p>
<h3>State persistence</h3>
<p>Long-running agents should be able to recover from interruption.</p>
<p>These aren't “AI features.”</p>
<p>They're software engineering fundamentals applied to AI systems.</p>
<hr />
<h2>9. Make Agent Behavior Observable</h2>
<p>Traditional applications are relatively deterministic.</p>
<p>Agents aren't.</p>
<p>The same request may result in different reasoning paths.</p>
<p>Therefore, logging only the final response isn't enough.</p>
<p>You need visibility into:</p>
<pre><code class="language-plaintext">Request
   ↓
Prompt / Context
   ↓
Model
   ↓
Decision
   ↓
Tool Call
   ↓
Tool Result
   ↓
Next Decision
   ↓
Final Response
</code></pre>
<p>Track metrics such as:</p>
<ul>
<li><p>Latency</p>
</li>
<li><p>Token usage</p>
</li>
<li><p>Cost</p>
</li>
<li><p>Tool-call frequency</p>
</li>
<li><p>Tool failures</p>
</li>
<li><p>Model failures</p>
</li>
<li><p>Task success rate</p>
</li>
<li><p>Escalation rate</p>
</li>
<li><p>Hallucination rate</p>
</li>
<li><p>Human intervention rate</p>
</li>
</ul>
<p>The goal is not merely:</p>
<blockquote>
<p>“The model responded.”</p>
</blockquote>
<p>The goal is:</p>
<blockquote>
<p><strong>“We understand why the agent behaved this way.”</strong></p>
</blockquote>
<hr />
<h2>10. Evaluation Is the Missing Layer</h2>
<p>Traditional software testing asks:</p>
<blockquote>
<p>“Does the function return the expected result?”</p>
</blockquote>
<p>Agent testing needs to ask much more.</p>
<p>For example:</p>
<pre><code class="language-plaintext">Did the agent understand the request?

Did it select the correct tool?

Did it retrieve the right information?

Did it follow authorization rules?

Did it avoid unnecessary actions?

Did it recover from tool failures?

Did it produce a correct final answer?
</code></pre>
<p>Create an evaluation dataset.</p>
<p>For example:</p>
<pre><code class="language-plaintext">100 representative requests
        ↓
Run Agent
        ↓
Expected Behavior
        ↓
Compare Results
        ↓
Measure Success
</code></pre>
<p>Then evaluate continuously.</p>
<p>Every time you change:</p>
<ul>
<li><p>The model</p>
</li>
<li><p>The system prompt</p>
</li>
<li><p>A tool</p>
</li>
<li><p>The RAG pipeline</p>
</li>
<li><p>The memory strategy</p>
</li>
<li><p>The orchestration logic</p>
</li>
</ul>
<p>run the evaluation suite again.</p>
<p>Otherwise, you're deploying based on intuition.</p>
<hr />
<h2>11. Don't Let the Agent Control Everything</h2>
<p>One of the most important architectural decisions is deciding <strong>what the LLM should control and what deterministic code should control</strong>.</p>
<p>A good principle is:</p>
<blockquote>
<p><strong>Let the LLM decide what requires reasoning. Let traditional software enforce what requires certainty.</strong></p>
</blockquote>
<p>For example:</p>
<h3>LLM</h3>
<pre><code class="language-plaintext">Intent classification
Planning
Summarization
Natural language understanding
Tool selection
</code></pre>
<h3>Deterministic code</h3>
<pre><code class="language-plaintext">Authentication
Authorization
Financial calculations
Data validation
Transaction boundaries
Business invariants
Security policies
</code></pre>
<p>Don't ask an LLM to enforce a rule that your application can enforce deterministically.</p>
<hr />
<h2>12. Introduce MCP Where It Actually Helps</h2>
<p>As agents grow, connecting them to dozens of systems becomes difficult.</p>
<p>This is where the <strong>Model Context Protocol (MCP)</strong> can become useful.</p>
<p>Instead of building tightly coupled integrations:</p>
<pre><code class="language-plaintext">Agent → CRM
Agent → GitHub
Agent → Database
Agent → Jira
Agent → Search
Agent → Internal APIs
</code></pre>
<p>you can expose capabilities through standardized interfaces.</p>
<p>Conceptually:</p>
<pre><code class="language-plaintext"> AI Agent
                    │
                   MCP
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
       CRM         Git         Jira
       MCP         MCP         MCP
</code></pre>
<p>But MCP isn't a replacement for security architecture.</p>
<p>Every tool still needs:</p>
<ul>
<li><p>Authentication</p>
</li>
<li><p>Authorization</p>
</li>
<li><p>Input validation</p>
</li>
<li><p>Auditing</p>
</li>
<li><p>Rate limits</p>
</li>
<li><p>Least-privilege access</p>
</li>
</ul>
<p>Standardized connectivity doesn't eliminate governance.</p>
<p>It makes governance even more important.</p>
<hr />
<h2>13. Resist the Multi-Agent Temptation</h2>
<p>Once your first agent works, the next temptation is obvious:</p>
<blockquote>
<p>“Let's create five agents!”</p>
</blockquote>
<p>Don't.</p>
<p>First ask:</p>
<p><strong>Why can't one agent solve the problem?</strong></p>
<p>Multi-agent architectures make sense when you have genuinely different responsibilities.</p>
<p>For example:</p>
<pre><code class="language-plaintext"> Supervisor
                        │
          ┌─────────────┼─────────────┐
          ▼             ▼             ▼
     Researcher     Developer     Reviewer
          │             │             │
          ▼             ▼             ▼
       Search          Code        Validation
</code></pre>
<p>But every additional agent introduces:</p>
<ul>
<li><p>More latency</p>
</li>
<li><p>More cost</p>
</li>
<li><p>More state</p>
</li>
<li><p>More failure modes</p>
</li>
<li><p>More coordination complexity</p>
</li>
<li><p>More observability requirements</p>
</li>
</ul>
<p>If a single well-designed agent works, keep it simple.</p>
<hr />
<h2>14. Move From Prototype to Production</h2>
<p>A useful maturity model looks like this:</p>
<h3>Level 1 — Prototype</h3>
<pre><code class="language-plaintext">LLM + Prompt
</code></pre>
<p>Goal:</p>
<p><strong>Prove the idea.</strong></p>
<hr />
<h3>Level 2 — Tool-Using Agent</h3>
<pre><code class="language-plaintext">LLM + Tools
</code></pre>
<p>Goal:</p>
<p><strong>Prove the workflow.</strong></p>
<hr />
<h3>Level 3 — Context-Aware Agent</h3>
<pre><code class="language-plaintext">LLM
+ Tools
+ RAG
+ Memory
</code></pre>
<p>Goal:</p>
<p><strong>Improve accuracy and usefulness.</strong></p>
<hr />
<h3>Level 4 — Production Agent</h3>
<pre><code class="language-plaintext">LLM
+ Tools
+ Context
+ State
+ Guardrails
+ Evaluation
+ Observability
+ Security
</code></pre>
<p>Goal:</p>
<p><strong>Make the system reliable.</strong></p>
<hr />
<h3>Level 5 — Agent Platform</h3>
<pre><code class="language-plaintext">Agent Runtime
      │
 ┌────┼─────┐
 ▼    ▼     ▼
Tools Memory Policies
 │
 ▼
Observability
 │
 ▼
Evaluation
 │
 ▼
Governance
</code></pre>
<p>Goal:</p>
<p><strong>Make agents scalable across the organization.</strong></p>
<hr />
<h2>15. A Practical Production Checklist</h2>
<p>Before deploying your first agent, ask:</p>
<h3>Architecture</h3>
<ul>
<li><p>Is the agent's responsibility clearly defined?</p>
</li>
<li><p>Can deterministic code handle part of the workflow?</p>
</li>
<li><p>Are tools separated from reasoning?</p>
</li>
</ul>
<h3>Security</h3>
<ul>
<li><p>Does the agent have least-privilege access?</p>
</li>
<li><p>Are sensitive operations protected?</p>
</li>
<li><p>Are all tool calls authorized?</p>
</li>
</ul>
<h3>Reliability</h3>
<ul>
<li><p>What happens when a tool fails?</p>
</li>
<li><p>What happens when the model times out?</p>
</li>
<li><p>Can the workflow resume after failure?</p>
</li>
<li><p>Are operations idempotent?</p>
</li>
</ul>
<h3>Evaluation</h3>
<ul>
<li><p>Do we have representative test cases?</p>
</li>
<li><p>Can we measure task success?</p>
</li>
<li><p>Do we test adversarial inputs?</p>
</li>
</ul>
<h3>Observability</h3>
<ul>
<li><p>Can we trace every tool call?</p>
</li>
<li><p>Can we measure latency and cost?</p>
</li>
<li><p>Can we understand why the agent failed?</p>
</li>
</ul>
<h3>Governance</h3>
<ul>
<li><p>Who owns the agent?</p>
</li>
<li><p>What data can it access?</p>
</li>
<li><p>What actions can it perform?</p>
</li>
<li><p>When must a human approve an action?</p>
</li>
</ul>
<p>If you can't answer these questions, you're probably still at the prototype stage.</p>
<hr />
<h2>16. The Real Architecture of an AI Agent</h2>
<p>After building several AI-powered systems, one lesson becomes increasingly clear:</p>
<p>The hard part isn't calling an LLM.</p>
<p>The hard part is engineering everything around it.</p>
<p>A production-grade agent looks more like:</p>
<pre><code class="language-plaintext"> ┌───────────────┐
                     │     User      │
                     └───────┬───────┘
                             │
                             ▼
                  ┌─────────────────────┐
                  │    Agent Runtime    │
                  └──────────┬──────────┘
                             │
          ┌──────────────────┼──────────────────┐
          ▼                  ▼                  ▼
     ┌─────────┐        ┌─────────┐        ┌──────────┐
     │   LLM   │        │ Memory  │        │   RAG    │
     └────┬────┘        └─────────┘        └──────────┘
          │
          ▼
     ┌─────────────┐
     │ Tool Layer  │
     └──────┬──────┘
            │
     ┌──────┼─────────┬──────────┐
     ▼      ▼         ▼          ▼
    APIs   DBs      MCP       Services
     
     ┌──────────────────────────────────┐
     │ Security • Evaluation • Logging  │
     │ Monitoring • Governance           │
     └──────────────────────────────────┘
</code></pre>
<p>The LLM is the reasoning engine.</p>
<p>It is <strong>not the entire architecture</strong>.</p>
<hr />
<h2>17. The Biggest Lesson</h2>
<p>Your first AI agent doesn't need to be autonomous.</p>
<p>It needs to be <strong>useful, measurable, controllable, and reliable</strong>.</p>
<p>Start small.</p>
<p>Give it one meaningful job.</p>
<p>Give it a few carefully designed tools.</p>
<p>Add context only when necessary.</p>
<p>Add memory only when it solves a real problem.</p>
<p>Introduce autonomy gradually.</p>
<p>Measure everything.</p>
<p>And put deterministic controls around probabilistic behavior.</p>
<p>The progression should be:</p>
<p><strong>Prototype → Tool Use → Context → Reliability → Evaluation → Governance → Production</strong></p>
<p>Not:</p>
<p><strong>Prototype → “Let's add 10 more agents.”</strong></p>
<p>The future of AI engineering isn't simply about building smarter models.</p>
<p>It's about building <strong>trustworthy systems around intelligent models</strong>.</p>
<p>And that's where traditional software engineering principles become more valuable—not less.</p>
<hr />
<h3>Final Thought</h3>
<p>The most important question when building your first AI agent isn't:</p>
<blockquote>
<p><strong>“How autonomous can we make it?”</strong></p>
</blockquote>
<p>It's:</p>
<blockquote>
<p><strong>“How much autonomy can we safely trust it with?”</strong></p>
</blockquote>
<p>That question changes the architecture.</p>
<p>And it changes everything that comes after the prototype.</p>
]]></content:encoded></item><item><title><![CDATA[AI Agents Are Not Chatbots: A Practical Guide to Building Autonomous AI Systems]]></title><description><![CDATA[What if the biggest mistake companies make with AI is treating agents like smarter chatbots?
Many organizations are investing heavily in Large Language Models (LLMs), yet they are still building syste]]></description><link>https://billliao.hashnode.dev/ai-agents-are-not-chatbots-a-practical-guide-to-building-autonomous-ai-systems</link><guid isPermaLink="true">https://billliao.hashnode.dev/ai-agents-are-not-chatbots-a-practical-guide-to-building-autonomous-ai-systems</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:11:13 GMT</pubDate><content:encoded><![CDATA[<p><strong>What if the biggest mistake companies make with AI is treating agents like smarter chatbots?</strong></p>
<p>Many organizations are investing heavily in Large Language Models (LLMs), yet they are still building systems that only answer questions.</p>
<p>The next generation of AI applications will not be defined by how well they generate text.</p>
<p>They will be defined by how well they <strong>reason, plan, act, learn, and collaborate with humans and other systems.</strong></p>
<p>That is the fundamental difference between a chatbot and an AI Agent.</p>
<hr />
<h3>The Chatbot Mindset vs. The Agent Mindset</h3>
<p>A traditional chatbot follows a simple interaction model:</p>
<pre><code class="language-plaintext">User → Prompt → LLM → Response
</code></pre>
<p>The LLM generates an answer based on the conversation context.</p>
<p>It is reactive.</p>
<p>It waits for instructions.</p>
<p>It does not own a goal.</p>
<p>An AI Agent works differently:</p>
<pre><code class="language-plaintext">Goal
 ↓
Reasoning Engine
 ↓
Planning
 ↓
Tool Selection
 ↓
Execution
 ↓
Observation
 ↓
Learning / Adjustment
 ↓
Final Result
</code></pre>
<p>An agent does not simply respond.</p>
<p>It decides <strong>what needs to happen next.</strong></p>
<hr />
<h2>What Makes an AI Agent Autonomous?</h2>
<p>Autonomy does not mean giving AI unlimited freedom.</p>
<p>A production-grade AI Agent requires several engineering capabilities.</p>
<h3>1. Goal-Oriented Reasoning</h3>
<p>A chatbot answers:</p>
<blockquote>
<p>"How do I migrate a database?"</p>
</blockquote>
<p>An agent thinks:</p>
<blockquote>
<p>"The goal is to migrate this database safely. I need to analyze the current schema, identify dependencies, create a migration plan, test compatibility, execute changes, and report risks."</p>
</blockquote>
<p>The difference is moving from:</p>
<p><strong>Answer generation → Goal achievement</strong></p>
<hr />
<h2>2. Planning and Task Decomposition</h2>
<p>Complex business problems are rarely single-step tasks.</p>
<p>For example:</p>
<p>"Prepare a customer onboarding report."</p>
<p>A chatbot might summarize information.</p>
<p>An AI Agent may:</p>
<ol>
<li><p>Retrieve customer data</p>
</li>
<li><p>Validate missing information</p>
</li>
<li><p>Query internal systems</p>
</li>
<li><p>Analyze compliance requirements</p>
</li>
<li><p>Generate documentation</p>
</li>
<li><p>Request human approval</p>
</li>
<li><p>Update downstream systems</p>
</li>
</ol>
<p>The agent transforms a high-level objective into executable workflows.</p>
<hr />
<h2>3. Tool Use: The Bridge Between AI and Enterprise Systems</h2>
<p>An LLM alone cannot:</p>
<ul>
<li><p>Access databases</p>
</li>
<li><p>Call APIs</p>
</li>
<li><p>Execute transactions</p>
</li>
<li><p>Search enterprise knowledge</p>
</li>
<li><p>Trigger workflows</p>
</li>
</ul>
<p>Agents become powerful when connected to tools.</p>
<p>A typical architecture:</p>
<pre><code class="language-plaintext"> User
                  |
                  v
             AI Agent
                  |
     +------------+------------+
     |            |            |
     v            v            v
  Database     APIs        Knowledge Base
     |            |            |
     +------------+------------+
                  |
                  v
              Business Action
</code></pre>
<p>Technologies such as:</p>
<ul>
<li><p>Model Context Protocol (MCP)</p>
</li>
<li><p>Function Calling</p>
</li>
<li><p>API Gateway</p>
</li>
<li><p>Enterprise Tool Registry</p>
</li>
</ul>
<p>are becoming critical building blocks for agent-based systems.</p>
<hr />
<h2>4. Memory: Moving Beyond Stateless Conversations</h2>
<p>Most chatbots have short-term memory:</p>
<blockquote>
<p>"Remember what I said earlier in this conversation."</p>
</blockquote>
<p>Agents need multiple types of memory:</p>
<h3>Short-Term Memory</h3>
<p>Current task context.</p>
<p>Example:</p>
<p>"The customer has submitted documents A and B."</p>
<h3>Long-Term Memory</h3>
<p>Historical knowledge.</p>
<p>Example:</p>
<p>"Previous interactions show this customer prefers digital communication."</p>
<h3>Organizational Memory</h3>
<p>Enterprise knowledge.</p>
<p>Example:</p>
<p>"These are the compliance rules approved by the legal team."</p>
<p>This is where technologies like:</p>
<ul>
<li><p>Vector Databases</p>
</li>
<li><p>Retrieval-Augmented Generation (RAG)</p>
</li>
<li><p>Knowledge Graphs</p>
</li>
</ul>
<p>become essential.</p>
<hr />
<h2>5. Multi-Agent Collaboration</h2>
<p>The future of AI systems will not be one giant AI model doing everything.</p>
<p>Instead, specialized agents will collaborate.</p>
<p>Example:</p>
<pre><code class="language-plaintext"> Supervisor Agent
                       |
        +--------------+--------------+
        |              |              |
        v              v              v

 Research Agent   Coding Agent   Testing Agent

        |              |              |

        +--------------+--------------+

                 Final Result
</code></pre>
<p>A software engineering workflow could include:</p>
<ul>
<li><p>Requirement Agent</p>
</li>
<li><p>Architecture Agent</p>
</li>
<li><p>Coding Agent</p>
</li>
<li><p>Security Agent</p>
</li>
<li><p>Testing Agent</p>
</li>
<li><p>Deployment Agent</p>
</li>
</ul>
<p>The role of humans shifts from writing every instruction to managing intelligent systems.</p>
<hr />
<h2>Building a Production-Ready AI Agent Architecture</h2>
<p>A practical enterprise architecture may look like this:</p>
<pre><code class="language-plaintext">+------------------------------------------------+
|                 User Interface                 |
| Web / Mobile / API / Chat                      |
+------------------------------------------------+

                     |
                     v

+------------------------------------------------+
|              Agent Orchestration Layer         |
|                                                |
| Planning | Reasoning | Memory | Decision Logic |
+------------------------------------------------+

                     |
                     v

+------------------------------------------------+
|                 AI Model Layer                 |
| GPT | Claude | Gemini | Llama | Mistral        |
+------------------------------------------------+

                     |
                     v

+------------------------------------------------+
|              Tool Integration Layer            |
| MCP | APIs | Database | Enterprise Systems    |
+------------------------------------------------+

                     |
                     v

+------------------------------------------------+
|              Governance Layer                 |
| Security | Audit | Evaluation | Monitoring     |
+------------------------------------------------+
</code></pre>
<p>The LLM is only one component.</p>
<p>The real engineering challenge is the system around it.</p>
<hr />
<h2>The Hard Problems Nobody Talks About</h2>
<p>Building an AI Agent demo is easy.</p>
<p>Building a reliable enterprise agent is hard.</p>
<p>The difficult problems are:</p>
<h3>1. Trust and Reliability</h3>
<p>How do we know the agent made the right decision?</p>
<p>We need:</p>
<ul>
<li><p>Evaluation frameworks</p>
</li>
<li><p>Human approval workflows</p>
</li>
<li><p>Confidence scoring</p>
</li>
<li><p>Guardrails</p>
</li>
</ul>
<hr />
<h3>2. Security</h3>
<p>Agents introduce a new security model.</p>
<p>Questions become:</p>
<ul>
<li><p>Which tools can an agent access?</p>
</li>
<li><p>Who authorizes actions?</p>
</li>
<li><p>How do we prevent prompt injection?</p>
</li>
<li><p>How do we audit decisions?</p>
</li>
</ul>
<p>AI security is becoming an extension of traditional distributed system security.</p>
<hr />
<h3>3. Observability</h3>
<p>Traditional applications have:</p>
<ul>
<li><p>Logs</p>
</li>
<li><p>Metrics</p>
</li>
<li><p>Traces</p>
</li>
</ul>
<p>Agents need more:</p>
<ul>
<li><p>Reasoning traces</p>
</li>
<li><p>Tool usage history</p>
</li>
<li><p>Decision paths</p>
</li>
<li><p>Model behavior analysis</p>
</li>
</ul>
<p>Understanding <strong>why an agent acted</strong> is as important as knowing <strong>what it did.</strong></p>
<hr />
<h2>AI Agents and Software Architecture</h2>
<p>AI Agents are not replacing software architecture.</p>
<p>They are creating a new architectural layer.</p>
<p>Traditional systems:</p>
<pre><code class="language-plaintext">User
 |
Application
 |
Database
</code></pre>
<p>Agentic systems:</p>
<pre><code class="language-plaintext">User
 |
AI Agent
 |
Application Services
 |
Enterprise Systems
</code></pre>
<p>The architecture challenge moves from:</p>
<p>"How do we build software that executes rules?"</p>
<p>to:</p>
<p>"How do we build systems that collaborate with intelligent decision makers?"</p>
<hr />
<h2>Practical Roadmap for Engineers</h2>
<p>If you want to build AI Agents, start here:</p>
<h3>Step 1: Master LLM Fundamentals</h3>
<p>Understand:</p>
<ul>
<li><p>Prompt engineering</p>
</li>
<li><p>Context windows</p>
</li>
<li><p>Embeddings</p>
</li>
<li><p>RAG</p>
</li>
<li><p>Model evaluation</p>
</li>
</ul>
<hr />
<h3>Step 2: Learn Agent Frameworks</h3>
<p>Explore:</p>
<ul>
<li><p>LangGraph</p>
</li>
<li><p>Semantic Kernel</p>
</li>
<li><p>AutoGen</p>
</li>
<li><p>CrewAI</p>
</li>
<li><p>Spring AI</p>
</li>
</ul>
<hr />
<h3>Step 3: Build Tool-Enabled Agents</h3>
<p>Connect agents with:</p>
<ul>
<li><p>REST APIs</p>
</li>
<li><p>Databases</p>
</li>
<li><p>Cloud services</p>
</li>
<li><p>Internal platforms</p>
</li>
</ul>
<hr />
<h3>Step 4: Add Enterprise Capabilities</h3>
<p>Focus on:</p>
<ul>
<li><p>Security</p>
</li>
<li><p>Governance</p>
</li>
<li><p>Observability</p>
</li>
<li><p>Cost management</p>
</li>
<li><p>Human-in-the-loop workflows</p>
</li>
</ul>
<hr />
<h2>The Future: From Software Applications to AI Organizations</h2>
<p>The biggest shift is not that AI will write more code.</p>
<p>The bigger shift is that software systems will become active participants.</p>
<p>Tomorrow's enterprise may look like:</p>
<ul>
<li><p>AI product managers analyzing requirements</p>
</li>
<li><p>AI developers creating features</p>
</li>
<li><p>AI testers validating releases</p>
</li>
<li><p>AI operations agents monitoring production</p>
</li>
<li><p>Humans supervising strategy and decisions</p>
</li>
</ul>
<p>The winners will not be companies that simply add chat interfaces to existing products.</p>
<p>They will be companies that redesign their systems around autonomous collaboration.</p>
<hr />
<h3>Final Thoughts</h3>
<p>AI Agents are not chatbots with extra features.</p>
<p>They represent a fundamental change in how software systems operate.</p>
<p>The next generation of engineering will require a new mindset:</p>
<p>From:</p>
<p><strong>"How do we build applications that respond?"</strong></p>
<p>To:</p>
<p><strong>"How do we build intelligent systems that achieve goals safely?"</strong></p>
<p>The future belongs to engineers who understand both:</p>
<p><strong>AI capabilities + Software Architecture principles.</strong></p>
<p>Because autonomous AI systems will not be built by AI researchers alone.</p>
<p>They will be built by software engineers who understand systems.</p>
]]></content:encoded></item><item><title><![CDATA[What 30 Years of Software Architecture Has Taught Me About Simplicity
]]></title><description><![CDATA[After 30 years of building software systems, I’ve learned one uncomfortable truth: the hardest systems to design are not the most complex ones — they are the ones that have lost their simplicity.
Why ]]></description><link>https://billliao.hashnode.dev/what-30-years-of-software-architecture-has-taught-me-about-simplicity</link><guid isPermaLink="true">https://billliao.hashnode.dev/what-30-years-of-software-architecture-has-taught-me-about-simplicity</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:10:29 GMT</pubDate><content:encoded><![CDATA[<p><strong>After 30 years of building software systems, I’ve learned one uncomfortable truth: the hardest systems to design are not the most complex ones — they are the ones that have lost their simplicity.</strong></p>
<p><strong>Why do we keep adding layers, frameworks, and abstractions when the real challenge is making software easier to understand?</strong></p>
<p><strong>Maybe the future of architecture is not about adding more sophistication. Maybe it is about removing everything that does not create value.</strong></p>
<hr />
<h3>The Complexity Trap: When “Enterprise Ready” Becomes Enterprise Heavy</h3>
<p>Early in my career, software systems were often constrained by hardware limitations, network bandwidth, and limited tooling.</p>
<p>Today, we have almost unlimited computing power, cloud platforms, containers, serverless technologies, AI assistants, and powerful frameworks.</p>
<p>Yet many systems are more difficult to maintain than ever.</p>
<p>Why?</p>
<p>Because we have confused <strong>capability with complexity</strong>.</p>
<p>A modern architecture diagram can easily become a collection of:</p>
<ul>
<li><p>Microservices</p>
</li>
<li><p>API gateways</p>
</li>
<li><p>Message brokers</p>
</li>
<li><p>Event streams</p>
</li>
<li><p>Service meshes</p>
</li>
<li><p>Distributed caches</p>
</li>
<li><p>Multiple databases</p>
</li>
<li><p>Observability platforms</p>
</li>
<li><p>AI agents</p>
</li>
</ul>
<p>Each technology solves a problem.</p>
<p>But every technology also creates new decisions, new failure modes, and new operational costs.</p>
<p>The question is not:</p>
<blockquote>
<p>“Can we build this architecture?”</p>
</blockquote>
<p>The question is:</p>
<blockquote>
<p>“Will this architecture make the business simpler or harder to change?”</p>
</blockquote>
<hr />
<h2>Lesson 1: Simplicity Is Not the Absence of Complexity</h2>
<p>One of the biggest misunderstandings about simplicity is that people think it means “simple code.”</p>
<p>It does not.</p>
<p>A simple system can still be:</p>
<ul>
<li><p>Highly scalable</p>
</li>
<li><p>Highly available</p>
</li>
<li><p>Distributed</p>
</li>
<li><p>Cloud-native</p>
</li>
<li><p>AI-enabled</p>
</li>
</ul>
<p>Simplicity is about reducing unnecessary cognitive load.</p>
<p>A good architecture allows engineers to answer questions quickly:</p>
<ul>
<li><p>Where does this business rule live?</p>
</li>
<li><p>How does data flow through the system?</p>
</li>
<li><p>What happens when something fails?</p>
</li>
<li><p>Where should I make this change?</p>
</li>
</ul>
<p>If every answer requires understanding ten services, five databases, and three messaging patterns, the architecture has failed its primary purpose.</p>
<hr />
<h2>Lesson 2: Good Architecture Protects Change</h2>
<p>Over the years, I have seen many teams optimize for the wrong thing.</p>
<p>They optimize for:</p>
<ul>
<li><p>The latest technology</p>
</li>
<li><p>The most fashionable architecture</p>
</li>
<li><p>The highest theoretical scalability</p>
</li>
</ul>
<p>But businesses rarely fail because they cannot handle one million requests per second.</p>
<p>They fail because they cannot adapt quickly enough.</p>
<p>The real test of architecture is:</p>
<blockquote>
<p>“How easy is it to change when the requirements change?”</p>
</blockquote>
<p>A system designed around change usually has:</p>
<ul>
<li><p>Clear boundaries</p>
</li>
<li><p>Explicit responsibilities</p>
</li>
<li><p>Stable interfaces</p>
</li>
<li><p>Low coupling</p>
</li>
<li><p>High cohesion</p>
</li>
</ul>
<p>This is why principles like:</p>
<ul>
<li><p>Domain-Driven Design</p>
</li>
<li><p>Hexagonal Architecture</p>
</li>
<li><p>Clean Architecture</p>
</li>
<li><p>Modular monoliths</p>
</li>
</ul>
<p>continue to remain valuable.</p>
<p>They are not about following patterns.</p>
<p>They are about preserving simplicity as systems evolve.</p>
<hr />
<h2>Lesson 3: The Best Abstraction Is the One You Can Remove</h2>
<p>Early engineers often learn:</p>
<blockquote>
<p>“Good software requires abstraction.”</p>
</blockquote>
<p>Experienced engineers learn:</p>
<blockquote>
<p>“Good software requires the right abstraction.”</p>
</blockquote>
<p>Every abstraction creates a mental model.</p>
<p>Too few abstractions create duplication.</p>
<p>Too many abstractions create confusion.</p>
<p>I have seen systems where developers needed to navigate:</p>
<p>Controller → Facade → Manager → Service → Handler → Adapter → Repository → DAO</p>
<p>before finding a piece of business logic.</p>
<p>The architecture looked professional.</p>
<p>The code review looked impressive.</p>
<p>But the system became difficult to understand.</p>
<p>A powerful question every architect should ask:</p>
<blockquote>
<p>“If I remove this abstraction, does the system become harder to change?”</p>
</blockquote>
<p>If the answer is no, maybe the abstraction does not belong.</p>
<hr />
<h2>Lesson 4: Architecture Is About Managing Trade-offs</h2>
<p>There is no perfect architecture.</p>
<p>Every decision has consequences.</p>
<p>Microservices provide:</p>
<p>✅ Independent deployment ✅ Team autonomy ✅ Technology flexibility</p>
<p>But they also introduce:</p>
<p>❌ Distributed complexity ❌ Network failures ❌ Operational overhead</p>
<p>A monolith provides:</p>
<p>✅ Simpler development ✅ Easier debugging ✅ Faster initial delivery</p>
<p>But it can become:</p>
<p>❌ Difficult to scale organizationally ❌ Hard to modify without boundaries</p>
<p>The mistake is not choosing the “wrong” architecture.</p>
<p>The mistake is choosing an architecture without understanding the trade-offs.</p>
<p>Great architects do not search for the perfect solution.</p>
<p>They search for the simplest solution that solves today's problem while keeping tomorrow's options open.</p>
<hr />
<h2>Lesson 5: AI Makes Simplicity Even More Important</h2>
<p>The rise of AI-powered development creates an interesting paradox.</p>
<p>AI can generate code faster than ever.</p>
<p>But faster code generation does not automatically create better systems.</p>
<p>In fact, AI can accelerate complexity.</p>
<p>AI agents can create:</p>
<ul>
<li><p>More services</p>
</li>
<li><p>More dependencies</p>
</li>
<li><p>More abstractions</p>
</li>
<li><p>More duplicated solutions</p>
</li>
</ul>
<p>The bottleneck is no longer writing code.</p>
<p>The bottleneck is maintaining architectural clarity.</p>
<p>In the AI era, the most valuable engineering skill may become:</p>
<p><strong>The ability to decide what should NOT be built.</strong></p>
<hr />
<h2>The Architecture Principles I Carry Forward</h2>
<p>After three decades in software engineering, these principles have become increasingly important:</p>
<h3>1. Start with the business problem, not the technology</h3>
<p>Technology should serve the architecture.</p>
<p>Architecture should serve the business.</p>
<hr />
<h3>2. Prefer evolution over revolution</h3>
<p>Most successful systems are not created by perfect designs.</p>
<p>They evolve through continuous improvement.</p>
<hr />
<h3>3. Make the common path easy</h3>
<p>Developers should not need to understand the entire system to make a small change.</p>
<hr />
<h3>4. Design boundaries before choosing tools</h3>
<p>Frameworks change.</p>
<p>Cloud providers change.</p>
<p>Programming languages change.</p>
<p>Good boundaries survive.</p>
<hr />
<h3>5. Optimize for human understanding</h3>
<p>Software is read far more often than it is written.</p>
<p>The next engineer who maintains your system is part of your architecture.</p>
<hr />
<h2>Final Thought</h2>
<p>After 30 years of software architecture, I no longer believe great systems are defined by how many technologies they use.</p>
<p>They are defined by how clearly they communicate their purpose.</p>
<p>The best architectures are not the ones that impress people during a presentation.</p>
<p>They are the ones that allow teams to move faster, make safer decisions, and evolve with confidence.</p>
<p><strong>Simplicity is not a beginner’s goal.</strong></p>
<p><strong>It is the result of experienced engineering judgment.</strong></p>
<hr />
<p>What has your experience taught you about keeping software systems simple as they grow?</p>
]]></content:encoded></item><item><title><![CDATA[The Hidden Complexity of Multi-Agent Systems]]></title><description><![CDATA["Adding more AI agents doesn't automatically create a smarter system. Sometimes it just creates a faster way to produce chaos."
Over the past year, AI agents have become the hottest topic in software ]]></description><link>https://billliao.hashnode.dev/the-hidden-complexity-of-multi-agent-systems</link><guid isPermaLink="true">https://billliao.hashnode.dev/the-hidden-complexity-of-multi-agent-systems</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:09:49 GMT</pubDate><content:encoded><![CDATA[<p><em>"Adding more AI agents doesn't automatically create a smarter system. Sometimes it just creates a faster way to produce chaos."</em></p>
<p>Over the past year, AI agents have become the hottest topic in software engineering.</p>
<p>Every conference demo seems to show a team of agents working together:</p>
<ul>
<li><p>A Planner Agent decomposes the task.</p>
</li>
<li><p>A Research Agent gathers information.</p>
</li>
<li><p>A Coding Agent writes code.</p>
</li>
<li><p>A Review Agent checks quality.</p>
</li>
<li><p>A Testing Agent validates the solution.</p>
</li>
</ul>
<p>At first glance, this looks like the future of software development.</p>
<p>But once these systems move from demos into production, many teams discover an uncomfortable truth:</p>
<p><strong>The difficult part isn't building an agent. It's coordinating many of them.</strong></p>
<hr />
<h3>Why Multi-Agent Systems Look So Simple</h3>
<p>Most tutorials describe multi-agent architectures like this:</p>
<pre><code class="language-plaintext">User Request
      │
      ▼
 Planner Agent
      │
 ┌────┴─────┐
 ▼          ▼
Agent A   Agent B
 │          │
 └────┬─────┘
      ▼
  Aggregator
</code></pre>
<p>This architecture works beautifully...</p>
<p>Until reality arrives.</p>
<p>Real production systems rarely execute perfectly.</p>
<p>Agents fail.</p>
<p>Tools become unavailable.</p>
<p>Context becomes outdated.</p>
<p>Two agents produce conflicting answers.</p>
<p>An LLM changes its behavior after a model update.</p>
<p>Suddenly the architecture becomes significantly more complicated.</p>
<hr />
<h2>Complexity #1 — Coordination</h2>
<p>Who decides what every agent should do?</p>
<p>Suppose a user asks:</p>
<blockquote>
<p>"Design a cloud migration strategy."</p>
</blockquote>
<p>A planner may generate ten subtasks.</p>
<p>Should they run:</p>
<ul>
<li><p>sequentially?</p>
</li>
<li><p>in parallel?</p>
</li>
<li><p>based on dependencies?</p>
</li>
<li><p>dynamically?</p>
</li>
</ul>
<p>What happens if Task 4 produces information that changes Tasks 6–10?</p>
<p>Do we restart?</p>
<p>Continue?</p>
<p>Re-plan?</p>
<p>Traditional software workflows are deterministic.</p>
<p>Agent workflows are probabilistic.</p>
<p>That difference changes everything.</p>
<hr />
<h2>Complexity #2 — Shared Context</h2>
<p>Every agent needs context.</p>
<p>But sharing <strong>all</strong> context with <strong>every</strong> agent quickly becomes impossible.</p>
<p>Imagine:</p>
<p>Planner: 10,000 tokens</p>
<p>Research: 15,000 tokens</p>
<p>Architecture: 18,000 tokens</p>
<p>Code Generation: 22,000 tokens</p>
<p>Testing: 12,000 tokens</p>
<p>Documentation: 15,000 tokens</p>
<p>Soon your context windows become enormous.</p>
<p>Larger prompts mean:</p>
<ul>
<li><p>slower execution</p>
</li>
<li><p>higher cost</p>
</li>
<li><p>increased hallucination risk</p>
</li>
<li><p>duplicated information</p>
</li>
</ul>
<p>Modern multi-agent systems therefore spend a surprising amount of engineering effort on <strong>context management</strong>, not model intelligence.</p>
<hr />
<h2>Complexity #3 — Memory</h2>
<p>Should agents remember previous work?</p>
<p>If yes:</p>
<p>Where?</p>
<p>How long?</p>
<p>Who owns the memory?</p>
<p>Consider a Coding Agent.</p>
<p>Should it remember:</p>
<ul>
<li><p>previous conversations?</p>
</li>
<li><p>coding style?</p>
</li>
<li><p>architectural decisions?</p>
</li>
<li><p>temporary debugging notes?</p>
</li>
<li><p>failed experiments?</p>
</li>
</ul>
<p>Long-term memory sounds useful...</p>
<p>Until outdated information starts influencing future decisions.</p>
<p>Memory becomes another distributed systems problem.</p>
<hr />
<h2>Complexity #4 — Conflicting Decisions</h2>
<p>Imagine two architecture agents.</p>
<p>One recommends:</p>
<p>Microservices.</p>
<p>Another recommends:</p>
<p>Modular Monolith.</p>
<p>Both provide convincing arguments.</p>
<p>Now what?</p>
<p>Large Language Models don't naturally "agree."</p>
<p>Someone—or something—must resolve conflicts.</p>
<p>Options include:</p>
<ul>
<li><p>voting</p>
</li>
<li><p>confidence scoring</p>
</li>
<li><p>ranking</p>
</li>
<li><p>arbitration agents</p>
</li>
<li><p>human approval</p>
</li>
</ul>
<p>Decision orchestration often becomes more complex than the original task.</p>
<hr />
<h2>Complexity #5 — Tool Contention</h2>
<p>Most agents don't just generate text.</p>
<p>They invoke tools:</p>
<ul>
<li><p>GitHub</p>
</li>
<li><p>Jira</p>
</li>
<li><p>Databases</p>
</li>
<li><p>Kubernetes</p>
</li>
<li><p>AWS</p>
</li>
<li><p>Slack</p>
</li>
<li><p>Internal APIs</p>
</li>
</ul>
<p>Now imagine six agents attempting to:</p>
<ul>
<li><p>modify the same file</p>
</li>
<li><p>deploy the same service</p>
</li>
<li><p>update the same ticket</p>
</li>
<li><p>execute incompatible commands</p>
</li>
</ul>
<p>Without coordination, agents become distributed race conditions.</p>
<p>This is less an AI problem than a classic concurrency problem.</p>
<hr />
<h2>Complexity #6 — Observability</h2>
<p>Debugging one LLM call is manageable.</p>
<p>Debugging fifty interacting agents?</p>
<p>Much harder.</p>
<p>When something fails, engineers need answers to questions like:</p>
<ul>
<li><p>Which agent started the workflow?</p>
</li>
<li><p>Which prompt caused the issue?</p>
</li>
<li><p>Which tool returned incorrect data?</p>
</li>
<li><p>Which decision propagated the error?</p>
</li>
<li><p>Which retry made the problem worse?</p>
</li>
</ul>
<p>Traditional application logs are no longer enough.</p>
<p>Production AI systems require:</p>
<ul>
<li><p>execution traces</p>
</li>
<li><p>prompt history</p>
</li>
<li><p>tool invocation logs</p>
</li>
<li><p>reasoning chains (where appropriate)</p>
</li>
<li><p>evaluation metrics</p>
</li>
<li><p>cost analysis</p>
</li>
</ul>
<p>Observability becomes a first-class architectural concern.</p>
<hr />
<h2>Complexity #7 — Cost Explosion</h2>
<p>One request.</p>
<p>Ten agents.</p>
<p>Five retries.</p>
<p>Twenty tool calls.</p>
<p>Several large-context prompts.</p>
<p>A workflow that initially looked inexpensive can become surprisingly costly.</p>
<p>Many production teams discover that orchestration—not inference—is the largest contributor to operational expenses.</p>
<p>Optimizing agent collaboration often delivers greater savings than switching to a cheaper model.</p>
<hr />
<h2>Complexity #8 — Trust</h2>
<p>Enterprise users don't simply ask:</p>
<p><em>"Did the AI answer correctly?"</em></p>
<p>They ask:</p>
<ul>
<li><p>Why did it choose this approach?</p>
</li>
<li><p>Which agent made this decision?</p>
</li>
<li><p>Which sources were consulted?</p>
</li>
<li><p>Can this result be audited?</p>
</li>
<li><p>Can I reproduce the workflow?</p>
</li>
</ul>
<p>As the number of agents grows, explaining the system's behavior becomes increasingly difficult.</p>
<p>Trust depends not only on intelligence, but also on transparency.</p>
<hr />
<h2>The Real Architecture Challenge</h2>
<p>Many teams focus on selecting the "best" model.</p>
<p>GPT-5.</p>
<p>Claude.</p>
<p>Gemini.</p>
<p>Qwen.</p>
<p>DeepSeek.</p>
<p>In reality, the model is often only one component of the system.</p>
<p>A production-ready multi-agent platform also needs:</p>
<ul>
<li><p>workflow orchestration</p>
</li>
<li><p>state management</p>
</li>
<li><p>memory architecture</p>
</li>
<li><p>context engineering</p>
</li>
<li><p>tool governance</p>
</li>
<li><p>observability</p>
</li>
<li><p>security</p>
</li>
<li><p>evaluation</p>
</li>
<li><p>cost optimization</p>
</li>
<li><p>failure recovery</p>
</li>
</ul>
<p>These capabilities determine whether a multi-agent system remains reliable as it scales.</p>
<hr />
<h2>A Different Way to Think About Multi-Agent Systems</h2>
<p>Instead of viewing agents as autonomous workers, consider them as <strong>distributed services with probabilistic behavior</strong>.</p>
<p>Many lessons from distributed systems still apply:</p>
<ul>
<li><p>Minimize unnecessary communication.</p>
</li>
<li><p>Keep responsibilities focused.</p>
</li>
<li><p>Design for retries and failures.</p>
</li>
<li><p>Avoid shared mutable state.</p>
</li>
<li><p>Make workflows observable.</p>
</li>
<li><p>Build deterministic control around non-deterministic intelligence.</p>
</li>
</ul>
<p>In other words, decades of software architecture experience remain highly relevant in the age of AI.</p>
<hr />
<h2>Final Thoughts</h2>
<p>The future of enterprise AI is unlikely to be powered by a single all-knowing agent.</p>
<p>Instead, we'll see ecosystems of specialized agents collaborating to solve increasingly complex problems.</p>
<p>But success won't come from simply adding more agents.</p>
<p>It will come from designing systems that can coordinate, observe, recover, and evolve reliably.</p>
<p>As with microservices a decade ago, the real challenge isn't decomposition—it's orchestration.</p>
<p><strong>The smartest multi-agent system isn't necessarily the one with the most agents. It's the one whose complexity is intentionally managed.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Designing APIs Around Business Capabilities]]></title><description><![CDATA[Most APIs are easy to build.
Very few are easy to evolve.
The difference usually isn't the framework, language, or cloud platform.
It's what the API is designed around.
Many teams unknowingly design A]]></description><link>https://billliao.hashnode.dev/designing-apis-around-business-capabilities</link><guid isPermaLink="true">https://billliao.hashnode.dev/designing-apis-around-business-capabilities</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:09:10 GMT</pubDate><content:encoded><![CDATA[<p>Most APIs are easy to build.</p>
<p>Very few are easy to evolve.</p>
<p>The difference usually isn't the framework, language, or cloud platform.</p>
<p>It's <strong>what the API is designed around</strong>.</p>
<p>Many teams unknowingly design APIs around their database schema. Others design them around UI screens. Both approaches work initially—but they often create tightly coupled systems that become increasingly difficult to change as the business grows.</p>
<p>The most resilient APIs are designed around <strong>business capabilities</strong>.</p>
<hr />
<h3>What Is a Business Capability?</h3>
<p>A business capability is something an organization can do to deliver value.</p>
<p>Examples include:</p>
<ul>
<li><p>Customer Onboarding</p>
</li>
<li><p>Order Management</p>
</li>
<li><p>Inventory Control</p>
</li>
<li><p>Payment Processing</p>
</li>
<li><p>Fraud Detection</p>
</li>
<li><p>Shipment Tracking</p>
</li>
<li><p>Subscription Management</p>
</li>
</ul>
<p>Notice something?</p>
<p>None of these describe tables.</p>
<p>None describe screens.</p>
<p>They describe <strong>business outcomes</strong>.</p>
<p>Your APIs should expose these capabilities—not your internal data model.</p>
<hr />
<h3>The Database-Driven API Trap</h3>
<p>A surprisingly common API design starts like this:</p>
<pre><code class="language-plaintext">GET /customers
GET /orders
GET /products
POST /payments
PUT /addresses
DELETE /users
</code></pre>
<p>Looks clean.</p>
<p>Looks RESTful.</p>
<p>But after a year, consumers begin asking:</p>
<blockquote>
<p>"How do I onboard a customer?"</p>
</blockquote>
<p>Now they need to call:</p>
<ul>
<li><p>Create Customer</p>
</li>
<li><p>Create Address</p>
</li>
<li><p>Upload Documents</p>
</li>
<li><p>Verify Identity</p>
</li>
<li><p>Accept Terms</p>
</li>
<li><p>Assign Account</p>
</li>
<li><p>Send Welcome Email</p>
</li>
</ul>
<p>One business action.</p>
<p>Seven API calls.</p>
<p>The client now understands your internal implementation.</p>
<p>That's coupling.</p>
<hr />
<h3>Business APIs Think Differently</h3>
<p>Instead of exposing database entities:</p>
<pre><code class="language-plaintext">Customer
Address
Document
Account
</code></pre>
<p>Expose business operations:</p>
<pre><code class="language-plaintext">POST /customer-onboarding

POST /orders/{id}/submit

POST /payments/{id}/authorize

POST /subscriptions/{id}/renew

POST /shipments/{id}/dispatch
</code></pre>
<p>The client says:</p>
<blockquote>
<p>"I want to onboard a customer."</p>
</blockquote>
<p>Not:</p>
<blockquote>
<p>"I need to create five related database records."</p>
</blockquote>
<p>The implementation becomes your responsibility—not the client's.</p>
<hr />
<h3>APIs Should Model Business Language</h3>
<p>One useful test is simple:</p>
<p>Can your product manager understand your API?</p>
<p>Imagine reading these endpoints aloud:</p>
<pre><code class="language-plaintext">POST /invoice/approve
POST /claim/review
POST /shipment/dispatch
POST /payment/refund
POST /customer-onboarding
</code></pre>
<p>Now compare with:</p>
<pre><code class="language-plaintext">POST /invoiceStatus
PUT /shipmentRecord
POST /paymentEntity
PATCH /customerTable
</code></pre>
<p>The first group speaks the language of the business.</p>
<p>The second speaks the language of the database.</p>
<p>Great APIs become part of the organization's ubiquitous language.</p>
<hr />
<h3>Why Capability-Based APIs Scale Better</h3>
<p>Business capabilities change more slowly than implementations.</p>
<p>Today:</p>
<pre><code class="language-plaintext">Customer
Address
Identity
Documents
</code></pre>
<p>Tomorrow:</p>
<pre><code class="language-plaintext">Customer
Address
Identity
Documents
Risk Score
Compliance Check
Fraud Screening
</code></pre>
<p>If clients call:</p>
<pre><code class="language-plaintext">POST /customer-onboarding
</code></pre>
<p>Nothing changes externally.</p>
<p>Internally, you can add:</p>
<ul>
<li><p>AI fraud detection</p>
</li>
<li><p>KYC providers</p>
</li>
<li><p>Credit scoring</p>
</li>
<li><p>Event publishing</p>
</li>
<li><p>Workflow orchestration</p>
</li>
</ul>
<p>Without breaking consumers.</p>
<p>That's the power of abstraction.</p>
<hr />
<h3>Better Alignment with Domain-Driven Design</h3>
<p>Capability-oriented APIs naturally align with Domain-Driven Design (DDD).</p>
<p>Instead of exposing persistence models:</p>
<pre><code class="language-plaintext">CustomerRepository
OrderRepository
InvoiceRepository
</code></pre>
<p>Expose domain behaviors:</p>
<pre><code class="language-plaintext">ApproveInvoice

PlaceOrder

CancelSubscription

ReserveInventory

IssueRefund
</code></pre>
<p>The API mirrors the domain model—not the storage model.</p>
<p>That's exactly where business logic belongs.</p>
<hr />
<h3>It Also Improves Microservice Boundaries</h3>
<p>Many microservices are actually "table services."</p>
<pre><code class="language-plaintext">Customer Service

Address Service

Product Service

Order Item Service
</code></pre>
<p>Soon every business process requires calls to six services.</p>
<p>Distributed monolith.</p>
<p>Instead, organize services around capabilities.</p>
<pre><code class="language-plaintext">Order Management

Fulfillment

Pricing

Payments

Customer Lifecycle
</code></pre>
<p>Each service owns a complete business responsibility.</p>
<p>Communication decreases.</p>
<p>Autonomy increases.</p>
<p>Change becomes localized.</p>
<hr />
<h3>Think in Workflows, Not Records</h3>
<p>Business users don't think:</p>
<blockquote>
<p>"Update Customer Row."</p>
</blockquote>
<p>They think:</p>
<ul>
<li><p>Activate account</p>
</li>
<li><p>Renew subscription</p>
</li>
<li><p>Cancel booking</p>
</li>
<li><p>Approve loan</p>
</li>
<li><p>Issue refund</p>
</li>
<li><p>Ship order</p>
</li>
</ul>
<p>Those are workflows.</p>
<p>APIs should represent workflows.</p>
<p>Internally, that workflow may update twenty tables.</p>
<p>Clients shouldn't know—or care.</p>
<hr />
<h3>Event-Driven Systems Benefit Too</h3>
<p>Capability-based APIs naturally produce meaningful events.</p>
<p>Instead of:</p>
<pre><code class="language-plaintext">CustomerUpdated
AddressUpdated
DocumentInserted
</code></pre>
<p>Publish:</p>
<pre><code class="language-plaintext">CustomerOnboarded

PaymentAuthorized

OrderSubmitted

InventoryReserved

ShipmentDispatched
</code></pre>
<p>Business events are stable.</p>
<p>Technical events change constantly.</p>
<p>Business events also make analytics, auditing, integrations, and AI agents significantly easier.</p>
<hr />
<h3>AI Agents Prefer Business Capabilities</h3>
<p>As AI agents become active participants in enterprise systems, capability-oriented APIs become even more valuable.</p>
<p>An AI agent can reason about:</p>
<pre><code class="language-plaintext">Create Refund

Approve Expense

Reserve Inventory

Generate Invoice
</code></pre>
<p>Much more effectively than:</p>
<pre><code class="language-plaintext">Insert Row

Update Status

Modify Table

Delete Record
</code></pre>
<p>The closer your API matches business intent, the easier it is for both humans and AI systems to orchestrate complex workflows safely.</p>
<p>The future API consumer may not be another application.</p>
<p>It may be an autonomous AI agent.</p>
<hr />
<h3>Questions to Ask During API Design</h3>
<p>Before publishing an endpoint, ask:</p>
<ul>
<li><p>Does this represent a business capability or a database table?</p>
</li>
<li><p>Would a business stakeholder understand this endpoint?</p>
</li>
<li><p>If our internal data model changes, will clients notice?</p>
</li>
<li><p>Does this endpoint complete a meaningful business action?</p>
</li>
<li><p>Are we exposing implementation details unnecessarily?</p>
</li>
<li><p>Could this API survive a database migration?</p>
</li>
</ul>
<p>If the answers make you uncomfortable...</p>
<p>You're probably designing around data instead of business.</p>
<hr />
<h3>Final Thoughts</h3>
<p>Databases evolve.</p>
<p>Frameworks evolve.</p>
<p>Cloud platforms evolve.</p>
<p>Even architectures evolve.</p>
<p>Business capabilities tend to remain remarkably stable.</p>
<p>The most successful APIs don't expose how the system works.</p>
<p>They expose <strong>what the business can do</strong>.</p>
<p>Design your APIs around capabilities.</p>
<p>Everything behind them is free to evolve.</p>
]]></content:encoded></item><item><title><![CDATA[Relational vs. NoSQL: How to Choose the Right Pattern for Scale]]></title><description><![CDATA["Should we migrate to NoSQL to scale?"
It's one of the most common questions in software architecture.
Ironically, it's often the wrong question.
Many engineering teams don't struggle because they cho]]></description><link>https://billliao.hashnode.dev/relational-vs-nosql-how-to-choose-the-right-pattern-for-scale</link><guid isPermaLink="true">https://billliao.hashnode.dev/relational-vs-nosql-how-to-choose-the-right-pattern-for-scale</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:08:32 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/97f449e2-b13e-4d9e-9e00-b1a1416c4c00.png" alt="" style="display:block;margin:0 auto" />

<p><strong>"Should we migrate to NoSQL to scale?"</strong></p>
<p>It's one of the most common questions in software architecture.</p>
<p>Ironically, it's often the wrong question.</p>
<p>Many engineering teams don't struggle because they chose PostgreSQL instead of MongoDB, or DynamoDB instead of MySQL.</p>
<p>They struggle because <strong>they chose the wrong data modeling pattern for their workload.</strong></p>
<p>The database is only the implementation. The <strong>data access pattern</strong> is the architecture.</p>
<p>After building enterprise systems for decades, I've learned one important lesson:</p>
<blockquote>
<p><strong>Scalability problems are usually modeling problems before they become database problems.</strong></p>
</blockquote>
<p>Let's explore how to choose the right persistence model.</p>
<hr />
<h2>First Principle: Scale the Access Pattern, Not the Database</h2>
<p>Before evaluating technologies, ask these questions:</p>
<ul>
<li><p>How is data written?</p>
</li>
<li><p>How is data read?</p>
</li>
<li><p>What consistency guarantees are required?</p>
</li>
<li><p>Does the system favor transactions or throughput?</p>
</li>
<li><p>Will queries evolve frequently?</p>
</li>
<li><p>Can data be duplicated safely?</p>
</li>
</ul>
<p>These questions matter far more than whether the database is relational or NoSQL.</p>
<hr />
<h2>When Relational Databases Are the Better Choice</h2>
<p>Relational databases remain the best solution for many large-scale systems.</p>
<p>They're ideal when your application requires:</p>
<p>✅ Strong ACID transactions</p>
<p>✅ Complex joins across multiple entities</p>
<p>✅ Referential integrity</p>
<p>✅ Flexible ad hoc queries</p>
<p>✅ Reporting and analytics</p>
<p>Examples include:</p>
<ul>
<li><p>Banking</p>
</li>
<li><p>Payments</p>
</li>
<li><p>ERP</p>
</li>
<li><p>HR systems</p>
</li>
<li><p>Healthcare</p>
</li>
<li><p>Financial platforms</p>
</li>
</ul>
<p>A well-designed PostgreSQL or SQL Server deployment can comfortably process millions of transactions every day.</p>
<p>The bottleneck is rarely SQL itself.</p>
<hr />
<h2>When NoSQL Wins</h2>
<p>NoSQL shines when your workload prioritizes scale over relational consistency.</p>
<p>Typical characteristics include:</p>
<ul>
<li><p>Massive write throughput</p>
</li>
<li><p>Huge datasets</p>
</li>
<li><p>Global distribution</p>
</li>
<li><p>Flexible schemas</p>
</li>
<li><p>Low-latency reads</p>
</li>
<li><p>Horizontal partitioning</p>
</li>
</ul>
<p>Common use cases:</p>
<ul>
<li><p>Social media feeds</p>
</li>
<li><p>IoT telemetry</p>
</li>
<li><p>Product catalogs</p>
</li>
<li><p>Recommendation engines</p>
</li>
<li><p>Session storage</p>
</li>
<li><p>Event logging</p>
</li>
<li><p>User activity tracking</p>
</li>
</ul>
<p>The key idea:</p>
<blockquote>
<p><strong>Data is modeled around access patterns—not relationships.</strong></p>
</blockquote>
<hr />
<h2>Think in Access Patterns</h2>
<p>Many architects begin with ER diagrams.</p>
<p>Modern distributed systems begin with questions like:</p>
<blockquote>
<p>"How will this data actually be queried?"</p>
</blockquote>
<p>For example:</p>
<p>Instead of:</p>
<p>Customer → Orders → Products → Payments</p>
<p>Think:</p>
<ul>
<li><p>Get customer profile</p>
</li>
<li><p>Show latest orders</p>
</li>
<li><p>Fetch recommendations</p>
</li>
<li><p>Display order history</p>
</li>
<li><p>Calculate dashboard metrics</p>
</li>
</ul>
<p>Every one of these becomes an optimized access pattern.</p>
<p>This mindset naturally leads toward denormalization where appropriate.</p>
<hr />
<h2>Normalization vs. Denormalization</h2>
<p>Relational databases encourage normalization.</p>
<p>Benefits include:</p>
<ul>
<li><p>Minimal duplication</p>
</li>
<li><p>Easier updates</p>
</li>
<li><p>Consistent data</p>
</li>
<li><p>Reduced storage</p>
</li>
</ul>
<p>NoSQL often embraces denormalization.</p>
<p>Benefits include:</p>
<ul>
<li><p>Single-query reads</p>
</li>
<li><p>Lower latency</p>
</li>
<li><p>Better horizontal scalability</p>
</li>
<li><p>Simpler distributed systems</p>
</li>
</ul>
<p>Neither approach is universally better.</p>
<p>The right choice depends on whether your bottleneck is:</p>
<ul>
<li><p>write complexity</p>
</li>
<li><p>read performance</p>
</li>
<li><p>storage cost</p>
</li>
<li><p>developer productivity</p>
</li>
</ul>
<hr />
<h2>The CAP Trade-off Still Matters</h2>
<p>Distributed systems force architectural compromises.</p>
<p>You cannot simultaneously maximize:</p>
<ul>
<li><p>Consistency</p>
</li>
<li><p>Availability</p>
</li>
<li><p>Partition tolerance</p>
</li>
</ul>
<p>Different databases optimize different trade-offs.</p>
<p>Examples:</p>
<p><strong>PostgreSQL</strong></p>
<p>Prioritizes strong consistency.</p>
<p><strong>MongoDB</strong></p>
<p>Balances flexibility with consistency.</p>
<p><strong>Cassandra</strong></p>
<p>Optimized for availability and massive scale.</p>
<p><strong>DynamoDB</strong></p>
<p>Built for predictable performance at internet scale.</p>
<p>There is no universally "best" database.</p>
<p>Only databases optimized for different problems.</p>
<hr />
<h2>CQRS Changes the Conversation</h2>
<p>One of the biggest architectural shifts over the past decade is separating reads from writes.</p>
<p>Instead of forcing one database to satisfy every requirement:</p>
<p>Write model:</p>
<ul>
<li><p>normalized</p>
</li>
<li><p>transactional</p>
</li>
<li><p>consistent</p>
</li>
</ul>
<p>Read model:</p>
<ul>
<li><p>denormalized</p>
</li>
<li><p>optimized</p>
</li>
<li><p>cached</p>
</li>
<li><p>replicated</p>
</li>
</ul>
<p>Suddenly...</p>
<p>You no longer need to choose between SQL and NoSQL.</p>
<p>You can use both.</p>
<p>This is why many modern architectures combine:</p>
<ul>
<li><p>PostgreSQL</p>
</li>
<li><p>Redis</p>
</li>
<li><p>Elasticsearch</p>
</li>
<li><p>DynamoDB</p>
</li>
<li><p>Kafka</p>
</li>
<li><p>Object Storage</p>
</li>
</ul>
<p>Each serves a specific responsibility.</p>
<hr />
<h2>Polyglot Persistence Is Becoming the Standard</h2>
<p>Large-scale systems increasingly use multiple persistence technologies together.</p>
<p>A typical architecture might look like this:</p>
<ul>
<li><p>PostgreSQL → transactional data</p>
</li>
<li><p>Redis → caching</p>
</li>
<li><p>Elasticsearch → search</p>
</li>
<li><p>Kafka → event streaming</p>
</li>
<li><p>S3/Object Storage → files</p>
</li>
<li><p>DynamoDB → high-scale lookups</p>
</li>
<li><p>Neo4j → graph relationships</p>
</li>
</ul>
<p>The database is selected based on the workload—not organizational preference.</p>
<hr />
<h2>A Practical Decision Framework</h2>
<p>When choosing a persistence strategy, ask:</p>
<p><strong>Choose Relational if:</strong></p>
<ul>
<li><p>You require ACID transactions.</p>
</li>
<li><p>Data relationships are complex.</p>
</li>
<li><p>Reporting is important.</p>
</li>
<li><p>Schema changes are controlled.</p>
</li>
<li><p>Data integrity is critical.</p>
</li>
</ul>
<p><strong>Choose NoSQL if:</strong></p>
<ul>
<li><p>Read/write throughput dominates.</p>
</li>
<li><p>Horizontal scaling is essential.</p>
</li>
<li><p>The schema changes frequently.</p>
</li>
<li><p>Latency is more important than joins.</p>
</li>
<li><p>Data can be denormalized.</p>
</li>
</ul>
<p><strong>Choose Both if:</strong></p>
<ul>
<li><p>Different workloads have different requirements.</p>
</li>
<li><p>You're implementing CQRS.</p>
</li>
<li><p>You're building event-driven systems.</p>
</li>
<li><p>You're operating at internet scale.</p>
</li>
</ul>
<hr />
<h2>Final Thoughts</h2>
<p>The question isn't:</p>
<blockquote>
<p><strong>"Should we use SQL or NoSQL?"</strong></p>
</blockquote>
<p>The better question is:</p>
<blockquote>
<p><strong>"What access pattern are we optimizing?"</strong></p>
</blockquote>
<p>Architecture is not about picking fashionable technologies.</p>
<p>It's about understanding data, workloads, consistency, and scale.</p>
<p>The most successful engineering teams don't argue over relational versus NoSQL.</p>
<p>They choose the right tool for each responsibility—and design their systems around how the business actually uses data.</p>
<p>That's how systems remain scalable, resilient, and maintainable as they grow.</p>
<hr />
<p><strong>What has your experience been?</strong></p>
<p>Have you scaled primarily with relational databases, embraced NoSQL, or adopted a polyglot persistence approach? I'd love to hear which architectural decisions have worked best for your systems.</p>
]]></content:encoded></item><item><title><![CDATA[AI Agents Are Changing Software Architecture]]></title><description><![CDATA[For years, software architecture has been built around a simple assumption: humans write code, systems execute it.
That assumption is breaking.
AI agents are no longer just autocomplete tools sitting ]]></description><link>https://billliao.hashnode.dev/ai-agents-are-changing-software-architecture</link><guid isPermaLink="true">https://billliao.hashnode.dev/ai-agents-are-changing-software-architecture</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:07:44 GMT</pubDate><content:encoded><![CDATA[<p>For years, software architecture has been built around a simple assumption: <strong>humans write code, systems execute it</strong>.</p>
<p>That assumption is breaking.</p>
<p>AI agents are no longer just autocomplete tools sitting inside an IDE. They can read requirements, generate code, run tests, call APIs, analyze logs, update tickets, and even collaborate with other agents. Once software gains the ability to <em>reason about work</em>, architecture itself must evolve.</p>
<p>The biggest change is not a new framework. It is a new participant in the system: <strong>an autonomous software actor</strong>.</p>
<hr />
<h3>The Old Architecture: Human-Centric Systems</h3>
<p>Traditional enterprise architecture is designed around human decision-makers.</p>
<p>A typical flow looks like this:</p>
<ul>
<li><p>User → UI</p>
</li>
<li><p>UI → Service</p>
</li>
<li><p>Service → Database</p>
</li>
<li><p>Developer → CI/CD → Production</p>
</li>
</ul>
<p>Humans define workflows, developers implement them, and systems execute deterministic logic. Automation exists, but it follows predefined rules.</p>
<p>This model works well when change is relatively slow and all meaningful decisions are made by people.</p>
<p>AI agents introduce a different dynamic.</p>
<hr />
<h3>The New Architecture: Agent-Centric Systems</h3>
<p>In an agent-enabled system, software components can initiate actions themselves.</p>
<p>A customer support agent may:</p>
<ol>
<li><p>Read an incoming message.</p>
</li>
<li><p>Retrieve customer history.</p>
</li>
<li><p>Decide which backend services to call.</p>
</li>
<li><p>Draft a response.</p>
</li>
<li><p>Trigger a refund workflow.</p>
</li>
<li><p>Escalate only if confidence is low.</p>
</li>
</ol>
<p>Notice what changed: <strong>the orchestration logic is no longer fully hardcoded</strong>.</p>
<p>The architecture shifts from:</p>
<p><strong>Request → Response</strong></p>
<p>to</p>
<p><strong>Goal → Plan → Actions → Evaluation → Outcome</strong></p>
<p>This is a fundamentally different execution model.</p>
<hr />
<h3>Five Architectural Changes Happening Right Now</h3>
<h3>1. APIs Become Agent Interfaces</h3>
<p>APIs were designed for developers. Agents need richer contracts:</p>
<ul>
<li><p>tool descriptions,</p>
</li>
<li><p>schemas,</p>
</li>
<li><p>permissions,</p>
</li>
<li><p>side-effect metadata,</p>
</li>
<li><p>rate limits,</p>
</li>
<li><p>safety constraints.</p>
</li>
</ul>
<p>This is why protocols such as the <strong>Model Context Protocol (MCP)</strong> are gaining attention. They expose capabilities in a machine-readable form so agents can discover and use tools dynamically.</p>
<p><strong>Design implication:</strong> API design must optimize for both humans and agents.</p>
<hr />
<h3>2. Context Becomes a First-Class Architectural Concern</h3>
<p>Traditional systems focus on data persistence. Agent systems focus on <strong>context persistence</strong>.</p>
<p>An agent may need:</p>
<ul>
<li><p>conversation history,</p>
</li>
<li><p>business rules,</p>
</li>
<li><p>user preferences,</p>
</li>
<li><p>previous actions,</p>
</li>
<li><p>retrieved documents,</p>
</li>
<li><p>execution traces.</p>
</li>
</ul>
<p>Architects now need a <strong>context layer</strong> alongside databases and caches.</p>
<p>Think of it as a new infrastructure component:</p>
<ul>
<li><p>Vector store</p>
</li>
<li><p>Memory store</p>
</li>
<li><p>Session context</p>
</li>
<li><p>Knowledge graph</p>
</li>
<li><p>Retrieval pipeline</p>
</li>
</ul>
<p>The question is no longer “Where is the data?” but “What context does the agent need to make a safe decision?”</p>
<hr />
<h3>3. Workflows Become Dynamic</h3>
<p>BPM engines and orchestration platforms traditionally execute predefined flows. Agents generate flows at runtime.</p>
<p>Instead of:</p>
<p>Step A → Step B → Step C</p>
<p>you get:</p>
<p>Goal → Agent decides next step</p>
<p>This requires architectures that support:</p>
<ul>
<li><p>tool registries,</p>
</li>
<li><p>policy engines,</p>
</li>
<li><p>execution sandboxes,</p>
</li>
<li><p>rollback mechanisms,</p>
</li>
<li><p>approval checkpoints.</p>
</li>
</ul>
<p>Deterministic systems are giving way to <strong>bounded autonomy</strong>.</p>
<hr />
<h3>4. Observability Must Include Reasoning</h3>
<p>Logs and metrics are no longer enough.</p>
<p>When an AI agent makes a decision, teams need to know:</p>
<ul>
<li><p>What prompt was used?</p>
</li>
<li><p>What context was retrieved?</p>
</li>
<li><p>Which tools were called?</p>
</li>
<li><p>Why was this action selected?</p>
</li>
<li><p>What confidence score was assigned?</p>
</li>
</ul>
<p>Modern observability stacks need <strong>AI traces</strong> in addition to request traces.</p>
<p>A production incident may involve a flawed reasoning chain, not a thrown exception.</p>
<hr />
<h3>5. Security Moves From Identity to Capability</h3>
<p>Traditional security asks: <em>Who is calling this service?</em></p>
<p>Agent security asks:</p>
<ul>
<li><p>What is this agent allowed to do?</p>
</li>
<li><p>Under which conditions?</p>
</li>
<li><p>With which data?</p>
</li>
<li><p>For how long?</p>
</li>
<li><p>With what level of human oversight?</p>
</li>
</ul>
<p>This leads to capability-based authorization, fine-grained tool permissions, and auditable action logs.</p>
<p>An agent with database access is effectively a semi-autonomous operator; treat it accordingly.</p>
<hr />
<h3>A Reference Architecture for Agentic Systems</h3>
<p>Here is a simplified enterprise pattern:</p>
<pre><code class="language-plaintext"> ┌──────────────────────┐
                │      User / App      │
                └──────────┬───────────┘
                           │
                    ┌──────▼──────┐
                    │ AI Gateway  │
                    └──────┬──────┘
                           │
            ┌──────────────┼──────────────┐
            │              │              │
      ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
      │ LLM Model │ │ Policy /  │ │ Context   │
      │ Provider  │ │ Guardrail │ │ Layer     │
      └─────┬─────┘ └─────┬─────┘ └─────┬─────┘
            │              │              │
            └──────┬───────┴───────┬──────┘
                   │               │
             ┌─────▼─────┐   ┌─────▼─────┐
             │ Tool / MCP│   │ Vector DB │
             │ Registry  │   │ Knowledge │
             └─────┬─────┘   └───────────┘
                   │
      ┌────────────┼────────────┐
      │            │            │
┌─────▼────┐ ┌────▼────┐ ┌─────▼────┐
│ Payments │ │ CRM API │ │ Ticketing│
└──────────┘ └─────────┘ └──────────┘
</code></pre>
<p>Notice the new layers that did not exist in most enterprise systems five years ago:</p>
<ul>
<li><p>AI Gateway,</p>
</li>
<li><p>Policy Engine,</p>
</li>
<li><p>Context Layer,</p>
</li>
<li><p>Tool Registry,</p>
</li>
<li><p>Vector Knowledge Store.</p>
</li>
</ul>
<p>These are becoming as important as API gateways and message brokers.</p>
<hr />
<h3>What This Means for Software Engineers</h3>
<p>The role of engineers is changing from <strong>implementing every step</strong> to <strong>designing reliable environments for autonomous execution</strong>.</p>
<p>Valuable skills increasingly include:</p>
<ul>
<li><p>API and tool design,</p>
</li>
<li><p>context engineering,</p>
</li>
<li><p>retrieval architecture,</p>
</li>
<li><p>evaluation frameworks,</p>
</li>
<li><p>guardrail design,</p>
</li>
<li><p>AI observability,</p>
</li>
<li><p>cost-aware inference architecture,</p>
</li>
<li><p>human-in-the-loop workflow design.</p>
</li>
</ul>
<p>Prompt engineering alone is not enough. System architecture becomes the competitive advantage.</p>
<hr />
<h3>The Biggest Mistake Companies Are Making</h3>
<p>Many organizations add an AI chatbot on top of an existing system and assume they have adopted AI architecture.</p>
<p>They have not.</p>
<p>If the surrounding platform cannot provide:</p>
<ul>
<li><p>trusted context,</p>
</li>
<li><p>safe tool access,</p>
</li>
<li><p>policy enforcement,</p>
</li>
<li><p>traceability,</p>
</li>
<li><p>evaluation,</p>
</li>
<li><p>rollback,</p>
</li>
<li><p>governance,</p>
</li>
</ul>
<p>then the AI component remains a demo, not an enterprise capability.</p>
<p>The hard problem is not generating text. The hard problem is building a <strong>trustworthy agent ecosystem</strong>.</p>
<hr />
<h3>The Next 3 Years</h3>
<p>I expect three major shifts:</p>
<h3>1. AI Gateways become standard enterprise infrastructure</h3>
<p>Just as API gateways became ubiquitous, AI gateways will centralize model access, policies, auditing, and cost management.</p>
<h3>2. MCP-style tool ecosystems become common</h3>
<p>Internal services will expose machine-readable capabilities specifically for agents.</p>
<h3>3. Architecture diagrams will include agents explicitly</h3>
<p>“Agent,” “memory,” “policy,” and “evaluation” boxes will appear alongside databases, queues, and services.</p>
<hr />
<h3>Final Thought</h3>
<p>Microservices changed how we decomposed systems.</p>
<p>Cloud-native changed how we deployed systems.</p>
<p>AI agents are changing <strong>who participates in the system</strong>.</p>
<p>That is a deeper architectural shift than most teams realize.</p>
<p>The question is no longer:</p>
<blockquote>
<p>“How do we add AI to our application?”</p>
</blockquote>
<p>The better question is:</p>
<blockquote>
<p>“How do we architect applications in a world where software can observe, reason, decide, and act?”</p>
</blockquote>
<p>The teams that answer that question well will define the next generation of enterprise architecture.</p>
]]></content:encoded></item><item><title><![CDATA[Cost-Driven Architecture: How to Audit and Optimize Your AWS Infrastructure Without Sacrificing Scale]]></title><description><![CDATA[The biggest scalability problem in cloud isn't technical—it's financial.
Most AWS architectures can scale.
The real question is: Can your cloud bill scale slower than your traffic?
Many engineering te]]></description><link>https://billliao.hashnode.dev/cost-driven-architecture-how-to-audit-and-optimize-your-aws-infrastructure-without-sacrificing-scale</link><guid isPermaLink="true">https://billliao.hashnode.dev/cost-driven-architecture-how-to-audit-and-optimize-your-aws-infrastructure-without-sacrificing-scale</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:07:10 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/5c3c5018-3687-4a79-a513-3f4d063252dc.png" alt="" style="display:block;margin:0 auto" />

<p><strong>The biggest scalability problem in cloud isn't technical—it's financial.</strong></p>
<p>Most AWS architectures can scale.</p>
<p>The real question is: <strong>Can your cloud bill scale slower than your traffic?</strong></p>
<p>Many engineering teams proudly build highly available, auto-scaling, multi-region systems. Six months later, finance asks a simple question:</p>
<blockquote>
<p>"Why did our AWS bill double when our customers only increased by 30%?"</p>
</blockquote>
<p>Silence.</p>
<p>Cloud platforms make scaling incredibly easy.</p>
<p>Unfortunately, they also make <strong>overspending</strong> incredibly easy.</p>
<p>The best architects today don't just optimize for performance, reliability, and security.</p>
<p>They optimize for <strong>cost efficiency</strong> as a first-class architectural concern.</p>
<p>Welcome to <strong>Cost-Driven Architecture</strong>.</p>
<hr />
<h2>The Shift: From Cloud-First to Cost-First</h2>
<p>A few years ago, the conversation was:</p>
<ul>
<li><p>Move everything to AWS</p>
</li>
<li><p>Build microservices</p>
</li>
<li><p>Auto-scale everything</p>
</li>
<li><p>Store everything</p>
</li>
<li><p>Never worry about servers again</p>
</li>
</ul>
<p>Today, the conversation has changed.</p>
<p>Engineering leaders now ask:</p>
<ul>
<li><p>Which services generate the highest AWS cost?</p>
</li>
<li><p>Which workloads actually need Kubernetes?</p>
</li>
<li><p>Are we over-provisioning databases?</p>
</li>
<li><p>How much does each customer cost us?</p>
</li>
<li><p>Can AI help reduce cloud waste?</p>
</li>
</ul>
<p>Cloud architecture is no longer measured only by uptime.</p>
<p>It is measured by <strong>cost per business transaction</strong>.</p>
<hr />
<h2>The Biggest Sources of AWS Waste</h2>
<p>After auditing numerous cloud environments, the same patterns appear repeatedly.</p>
<h3>1. Over-Provisioned EC2 Instances</h3>
<p>Many production servers operate at:</p>
<ul>
<li><p>5% CPU</p>
</li>
<li><p>10% memory</p>
</li>
<li><p>24/7 uptime</p>
</li>
</ul>
<p>A team selected an instance size three years ago and never revisited it.</p>
<p>Common fixes include:</p>
<ul>
<li><p>Right-size instance families</p>
</li>
<li><p>Adopt Graviton processors where supported</p>
</li>
<li><p>Schedule non-production environments</p>
</li>
<li><p>Enable Auto Scaling</p>
</li>
<li><p>Replace long-running instances with serverless workloads where appropriate</p>
</li>
</ul>
<p>Small changes often reduce compute costs by <strong>30–60%</strong>.</p>
<hr />
<h3>2. Idle Kubernetes Clusters</h3>
<p>Kubernetes is powerful.</p>
<p>It is also expensive.</p>
<p>Many organizations deploy Amazon EKS for applications that could easily run on:</p>
<ul>
<li><p>AWS Lambda</p>
</li>
<li><p>Amazon ECS with Fargate</p>
</li>
<li><p>App Runner</p>
</li>
<li><p>Simple EC2 Auto Scaling Groups</p>
</li>
</ul>
<p>Running Kubernetes because "everyone else does" is rarely a cost-effective architectural decision.</p>
<p>The best platform is the simplest one that satisfies your operational needs.</p>
<hr />
<h3>3. Storage That Nobody Uses</h3>
<p>Storage silently grows every day.</p>
<p>Typical examples include:</p>
<ul>
<li><p>Unused EBS volumes</p>
</li>
<li><p>Old EBS snapshots</p>
</li>
<li><p>Multiple copies of backups</p>
</li>
<li><p>Forgotten S3 buckets</p>
</li>
<li><p>Old log archives</p>
</li>
</ul>
<p>Storage costs rarely trigger alarms individually.</p>
<p>Collectively, they become surprisingly large.</p>
<p>Lifecycle policies and automated cleanup are some of the highest ROI improvements available.</p>
<hr />
<h3>4. Data Transfer Costs</h3>
<p>Many teams monitor compute costs carefully.</p>
<p>Few monitor network costs.</p>
<p>Cross-AZ communication, NAT Gateways, cross-region replication, and internet egress frequently become significant expenses.</p>
<p>Architectural decisions such as:</p>
<ul>
<li><p>Keeping services in the same Availability Zone when appropriate</p>
</li>
<li><p>Using VPC endpoints</p>
</li>
<li><p>Reducing unnecessary cross-region traffic</p>
</li>
<li><p>Leveraging CDNs effectively</p>
</li>
</ul>
<p>can dramatically reduce networking costs.</p>
<hr />
<h3>5. Databases Running at 5% Utilization</h3>
<p>Databases are often oversized "just in case."</p>
<p>Typical issues include:</p>
<ul>
<li><p>Large RDS instances with minimal traffic</p>
</li>
<li><p>Read replicas that are never queried</p>
</li>
<li><p>Provisioned IOPS without corresponding workloads</p>
</li>
<li><p>Overpowered Aurora clusters</p>
</li>
</ul>
<p>Database right-sizing often delivers immediate savings without affecting users.</p>
<hr />
<h2>Build a Cost Audit Framework</h2>
<p>Instead of reacting to monthly invoices, perform continuous architectural reviews.</p>
<p>A practical framework includes:</p>
<h3>Compute</h3>
<p>Ask:</p>
<ul>
<li><p>Are instances correctly sized?</p>
</li>
<li><p>Are Spot Instances appropriate?</p>
</li>
<li><p>Can workloads move to serverless?</p>
</li>
<li><p>Are containers over-allocated?</p>
</li>
<li><p>Are Auto Scaling policies effective?</p>
</li>
</ul>
<hr />
<h3>Storage</h3>
<p>Review:</p>
<ul>
<li><p>S3 lifecycle rules</p>
</li>
<li><p>Intelligent-Tiering adoption</p>
</li>
<li><p>Snapshot retention</p>
</li>
<li><p>EBS utilization</p>
</li>
<li><p>Backup frequency</p>
</li>
</ul>
<hr />
<h3>Networking</h3>
<p>Measure:</p>
<ul>
<li><p>NAT Gateway charges</p>
</li>
<li><p>Cross-region traffic</p>
</li>
<li><p>Cross-AZ communication</p>
</li>
<li><p>CloudFront cache hit rates</p>
</li>
<li><p>Internet egress</p>
</li>
</ul>
<hr />
<h3>Databases</h3>
<p>Review:</p>
<ul>
<li><p>CPU utilization</p>
</li>
<li><p>Memory usage</p>
</li>
<li><p>Connection counts</p>
</li>
<li><p>Replica utilization</p>
</li>
<li><p>Storage growth</p>
</li>
</ul>
<hr />
<h3>Observability</h3>
<p>Logging can become one of the fastest-growing cloud expenses.</p>
<p>Questions to ask:</p>
<ul>
<li><p>Do we retain logs longer than required?</p>
</li>
<li><p>Are debug logs enabled in production?</p>
</li>
<li><p>Can metrics replace high-volume logging?</p>
</li>
<li><p>Are traces sampled intelligently?</p>
</li>
</ul>
<p>Good observability captures useful signals—not every possible event.</p>
<hr />
<h2>Design Principles for Cost-Driven Architecture</h2>
<h3>Design for Elasticity</h3>
<p>Resources should expand and contract automatically with demand.</p>
<p>Idle infrastructure is one of the most common forms of cloud waste.</p>
<hr />
<h3>Design for Simplicity</h3>
<p>Every managed service introduces operational complexity and recurring cost.</p>
<p>Choose the simplest architecture that meets current business requirements.</p>
<p>Premature complexity often carries a permanent price tag.</p>
<hr />
<h3>Design for Visibility</h3>
<p>If you cannot attribute cloud costs to products, teams, or customers, optimization becomes guesswork.</p>
<p>Tag resources consistently and expose cost dashboards to engineering teams—not just finance.</p>
<p>Engineers make better decisions when cost is visible alongside performance metrics.</p>
<hr />
<h3>Design for Automation</h3>
<p>Manual cleanup rarely scales.</p>
<p>Automate:</p>
<ul>
<li><p>Idle resource detection</p>
</li>
<li><p>Snapshot retention</p>
</li>
<li><p>Budget alerts</p>
</li>
<li><p>Resource tagging</p>
</li>
<li><p>Scheduled shutdowns</p>
</li>
<li><p>Rightsizing recommendations</p>
</li>
</ul>
<p>Automation prevents waste from reappearing.</p>
<hr />
<h2>AI Is Becoming Your Cloud Cost Optimizer</h2>
<p>AI is changing cloud operations in fascinating ways.</p>
<p>Modern AI agents can:</p>
<ul>
<li><p>Detect idle resources automatically</p>
</li>
<li><p>Identify oversized infrastructure</p>
</li>
<li><p>Recommend Reserved Instance or Savings Plan purchases</p>
</li>
<li><p>Forecast monthly cloud spend</p>
</li>
<li><p>Predict traffic growth</p>
</li>
<li><p>Simulate infrastructure changes before deployment</p>
</li>
</ul>
<p>Instead of reviewing hundreds of dashboards, engineers can ask:</p>
<blockquote>
<p>"Which services increased AWS costs by more than 20% this week, and why?"</p>
</blockquote>
<p>That is the future of cloud operations.</p>
<hr />
<h2>Metrics That Actually Matter</h2>
<p>Successful engineering organizations increasingly monitor metrics such as:</p>
<ul>
<li><p>Cost per API request</p>
</li>
<li><p>Cost per customer</p>
</li>
<li><p>Cost per tenant</p>
</li>
<li><p>Cost per order</p>
</li>
<li><p>Cost per deployment</p>
</li>
<li><p>Cost per GB processed</p>
</li>
<li><p>Cost per AI inference</p>
</li>
</ul>
<p>These business-oriented metrics reveal architectural inefficiencies far more effectively than a monthly invoice.</p>
<hr />
<h2>Final Thoughts</h2>
<p>Scalable architecture is no longer just about handling millions of requests.</p>
<p>It is about handling millions of requests <strong>efficiently</strong>.</p>
<p>Every architecture decision has a financial consequence.</p>
<p>Choosing the right database, compute platform, storage strategy, networking topology, or deployment model can save millions over the lifetime of a system.</p>
<p>The strongest architects today think beyond latency, throughput, and availability.</p>
<p>They also ask:</p>
<blockquote>
<p><strong>"What is the cost of every architectural decision we make?"</strong></p>
</blockquote>
<p>Because in modern cloud engineering, <strong>the most elegant architecture isn't necessarily the fastest—it's the one that delivers the greatest business value for every dollar spent.</strong></p>
<hr />
<h3>Key Takeaways</h3>
<ul>
<li><p>Treat cost as a first-class architectural requirement.</p>
</li>
<li><p>Continuously audit compute, storage, networking, and databases.</p>
</li>
<li><p>Right-size resources based on actual utilization.</p>
</li>
<li><p>Automate cost optimization wherever possible.</p>
</li>
<li><p>Measure cloud efficiency using business metrics—not just infrastructure metrics.</p>
</li>
<li><p>Use AI to identify waste and recommend optimizations before costs spiral out of control.</p>
</li>
</ul>
<p><strong>What has been the biggest source of unexpected AWS costs in your experience?</strong> Let's discuss in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[Common NoSQL Modeling Patterns Every Senior Backend Developer Should Know]]></title><description><![CDATA["If you're still designing NoSQL databases like relational databases, you're probably losing most of their advantages."
Many developers learn MongoDB, DynamoDB, Cassandra, or Couchbase by replacing SQ]]></description><link>https://billliao.hashnode.dev/common-nosql-modeling-patterns-every-senior-backend-developer-should-know</link><guid isPermaLink="true">https://billliao.hashnode.dev/common-nosql-modeling-patterns-every-senior-backend-developer-should-know</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:06:19 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/7e9f327d-5026-4f7d-b976-7428e6e17e97.png" alt="" style="display:block;margin:0 auto" />

<p><em>"If you're still designing NoSQL databases like relational databases, you're probably losing most of their advantages."</em></p>
<p>Many developers learn MongoDB, DynamoDB, Cassandra, or Couchbase by replacing SQL syntax with document queries.</p>
<p>But that's not how NoSQL databases are meant to be used.</p>
<p>The biggest mindset shift isn't learning a new query language.</p>
<p>It's learning to model data around <strong>how the application reads and writes data</strong>, rather than around normalization.</p>
<p>After working on high-throughput distributed systems, I've found that experienced backend engineers usually rely on a small set of proven modeling patterns instead of inventing a schema from scratch every time.</p>
<p>Here are the ones every senior backend developer should know.</p>
<hr />
<h2>1. Aggregate Pattern</h2>
<p>Instead of splitting related data across multiple tables, store data that changes together in a single document.</p>
<p>Traditional SQL:</p>
<pre><code class="language-plaintext">Customer
Orders
OrderItems
Addresses
Payments
</code></pre>
<p>NoSQL:</p>
<pre><code class="language-plaintext">Customer
 ├── Profile
 ├── Addresses
 ├── Preferences
 └── Recent Orders
</code></pre>
<p>Benefits:</p>
<ul>
<li><p>One database read</p>
</li>
<li><p>Atomic updates</p>
</li>
<li><p>No joins</p>
</li>
<li><p>Better performance</p>
</li>
</ul>
<p>This is one of the fundamental principles of document databases.</p>
<p><strong>Rule of thumb:</strong></p>
<blockquote>
<p>Data that changes together should live together.</p>
</blockquote>
<hr />
<h2>2. Denormalization Pattern</h2>
<p>In relational databases we avoid duplication.</p>
<p>In NoSQL we often embrace it.</p>
<p>Example:</p>
<p>Instead of</p>
<pre><code class="language-plaintext">Order
Customer
</code></pre>
<p>every order can include:</p>
<pre><code class="language-plaintext">Order
{
    customerId,
    customerName,
    customerTier,
    shippingAddress
}
</code></pre>
<p>Why?</p>
<p>Because reads are far more common than updates.</p>
<p>Duplicating a few fields eliminates expensive lookups.</p>
<p>Storage is usually cheaper than latency.</p>
<hr />
<h2>3. One-to-Many Embedding</h2>
<p>Small collections should be embedded.</p>
<p>Example:</p>
<pre><code class="language-plaintext">Blog Post
 ├── Title
 ├── Content
 └── Comments[]
</code></pre>
<p>Perfect when:</p>
<ul>
<li><p>comments are limited</p>
</li>
<li><p>always retrieved together</p>
</li>
<li><p>updated with the parent</p>
</li>
</ul>
<p>Avoid embedding if the child collection can grow without bound.</p>
<hr />
<h2>4. Reference Pattern</h2>
<p>Large relationships should use references.</p>
<p>Instead of</p>
<pre><code class="language-plaintext">Customer
 └── Orders[]
</code></pre>
<p>Store</p>
<pre><code class="language-plaintext">Customer
Order
Order
Order
</code></pre>
<p>linked by</p>
<pre><code class="language-plaintext">customerId
</code></pre>
<p>Ideal for:</p>
<ul>
<li><p>millions of child records</p>
</li>
<li><p>independent updates</p>
</li>
<li><p>pagination</p>
</li>
</ul>
<hr />
<h2>5. Bucket Pattern</h2>
<p>High-volume time-series data can overwhelm document limits.</p>
<p>Instead of</p>
<pre><code class="language-plaintext">Temperature Reading
Temperature Reading
Temperature Reading
</code></pre>
<p>Group them into buckets.</p>
<pre><code class="language-plaintext">Sensor

Bucket
{
   hour:10,
   readings:[...]
}

Bucket
{
   hour:11,
   readings:[...]
}
</code></pre>
<p>Benefits:</p>
<ul>
<li><p>fewer documents</p>
</li>
<li><p>faster queries</p>
</li>
<li><p>lower storage overhead</p>
</li>
</ul>
<p>Widely used in IoT, monitoring, telemetry, and logging systems.</p>
<hr />
<h2>6. Time-Series Pattern</h2>
<p>When data naturally grows over time:</p>
<pre><code class="language-plaintext">Logs
Events
Metrics
Transactions
</code></pre>
<p>Design around timestamps.</p>
<p>Common partition key:</p>
<pre><code class="language-plaintext">DeviceID + Date

or

UserID + Month
</code></pre>
<p>Benefits:</p>
<ul>
<li><p>efficient range queries</p>
</li>
<li><p>predictable partition sizes</p>
</li>
<li><p>easy archival</p>
</li>
</ul>
<hr />
<h2>7. Computed Pattern</h2>
<p>Instead of calculating expensive values repeatedly...</p>
<p>Store them.</p>
<p>Example:</p>
<pre><code class="language-plaintext">Product

averageRating
reviewCount
salesRank
inventoryValue
</code></pre>
<p>Update them whenever new events occur.</p>
<p>This reduces CPU usage and dramatically improves read performance.</p>
<p>Perfect for dashboards.</p>
<hr />
<h2>8. Materialized View Pattern</h2>
<p>Different users need different data.</p>
<p>Rather than one universal schema...</p>
<p>Maintain multiple optimized views.</p>
<p>Example:</p>
<p>Customer Profile</p>
<pre><code class="language-plaintext">{
    name,
    orders,
    loyalty
}
</code></pre>
<p>Admin Dashboard</p>
<pre><code class="language-plaintext">{
    customer,
    totalRevenue,
    refunds,
    supportTickets
}
</code></pre>
<p>Analytics</p>
<pre><code class="language-plaintext">{
    country,
    spending,
    monthlyGrowth
}
</code></pre>
<p>Each model is optimized for its consumers.</p>
<hr />
<h2>9. Outlier Pattern</h2>
<p>Sometimes one document becomes enormous.</p>
<p>Example:</p>
<p>Most blog posts have</p>
<pre><code class="language-plaintext">20 comments
</code></pre>
<p>One viral post has</p>
<pre><code class="language-plaintext">250,000 comments
</code></pre>
<p>Instead of making every document huge:</p>
<pre><code class="language-plaintext">Post

Comments
</code></pre>
<p>Only move exceptional cases into separate collections.</p>
<p>Optimize for the common case.</p>
<hr />
<h2>10. Attribute Pattern</h2>
<p>When entities have highly variable attributes.</p>
<p>Example:</p>
<p>Instead of</p>
<pre><code class="language-plaintext">Phone
Laptop
Camera
TV
</code></pre>
<p>each having different columns...</p>
<p>Store attributes dynamically.</p>
<pre><code class="language-plaintext">{
   name:"Laptop",
   attributes:[
      {name:"RAM",value:"32GB"},
      {name:"CPU",value:"M4"}
   ]
}
</code></pre>
<p>Perfect for:</p>
<ul>
<li><p>product catalogs</p>
</li>
<li><p>CMS</p>
</li>
<li><p>configurable systems</p>
</li>
</ul>
<hr />
<h2>11. Subset Pattern</h2>
<p>Not every query needs the full document.</p>
<p>Keep frequently accessed fields together.</p>
<p>Example:</p>
<pre><code class="language-plaintext">Article
{
    title,
    summary,
    author,
    thumbnail
}
</code></pre>
<p>Large content can live elsewhere.</p>
<p>Homepage queries become dramatically faster.</p>
<hr />
<h2>12. Event Sourcing Pattern</h2>
<p>Instead of storing only current state...</p>
<p>Store every change.</p>
<pre><code class="language-plaintext">Account Opened

Money Deposited

Money Withdrawn

Interest Applied
</code></pre>
<p>Current balance is rebuilt from events.</p>
<p>Benefits:</p>
<ul>
<li><p>complete audit history</p>
</li>
<li><p>replay capability</p>
</li>
<li><p>temporal queries</p>
</li>
</ul>
<p>Popular in financial systems and distributed architectures.</p>
<hr />
<h2>Choosing the Right Pattern</h2>
<p>The best NoSQL schema starts with your application's access patterns.</p>
<p>Ask these questions first:</p>
<ul>
<li><p>What are the most frequent queries?</p>
</li>
<li><p>What data changes together?</p>
</li>
<li><p>What needs atomic updates?</p>
</li>
<li><p>How large can this collection grow?</p>
</li>
<li><p>What latency do users expect?</p>
</li>
<li><p>What data can be duplicated safely?</p>
</li>
<li><p>Which fields are queried most often?</p>
</li>
</ul>
<p>If you design around these questions, the right pattern usually becomes obvious.</p>
<hr />
<h2>Final Thoughts</h2>
<p>NoSQL modeling isn't about removing tables.</p>
<p>It's about optimizing data for how the business actually uses it.</p>
<p>Senior engineers understand that there is no universal "best" schema. The right design depends on access patterns, scalability requirements, consistency needs, and operational constraints.</p>
<p>The strongest backend systems often combine multiple modeling patterns to balance performance, maintainability, and flexibility.</p>
<p>When you stop thinking in terms of normalization and start thinking in terms of application behavior, NoSQL databases reveal their true strengths.</p>
<hr />
<p><strong>Which NoSQL modeling pattern has saved your team the most performance or scalability headaches? Share your experience in the comments.</strong></p>
]]></content:encoded></item><item><title><![CDATA[TypeScript in 2026: Why End-to-End Type Safety Is No Longer Optional for Full-Stack Teams]]></title><description><![CDATA[For years, TypeScript was treated as a "better JavaScript."
In 2026, that mindset is outdated.
Today, TypeScript is no longer just a language—it has become the backbone of modern full-stack engineerin]]></description><link>https://billliao.hashnode.dev/typescript-in-2026-why-end-to-end-type-safety-is-no-longer-optional-for-full-stack-teams</link><guid isPermaLink="true">https://billliao.hashnode.dev/typescript-in-2026-why-end-to-end-type-safety-is-no-longer-optional-for-full-stack-teams</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:05:29 GMT</pubDate><content:encoded><![CDATA[<p>For years, TypeScript was treated as a "better JavaScript."</p>
<p>In 2026, that mindset is outdated.</p>
<p>Today, TypeScript is no longer just a language—it has become the backbone of modern full-stack engineering. The most successful engineering teams aren't simply writing TypeScript. They're building systems where types flow seamlessly from the database all the way to the browser.</p>
<p>The result?</p>
<ul>
<li><p>Fewer production bugs</p>
</li>
<li><p>Faster development</p>
</li>
<li><p>Safer refactoring</p>
</li>
<li><p>Better AI-generated code</p>
</li>
<li><p>More confident deployments</p>
</li>
</ul>
<p>The biggest architectural shift isn't another frontend framework.</p>
<p>It's <strong>end-to-end type safety</strong>.</p>
<hr />
<h3>The Hidden Cost of Broken Types</h3>
<p>A typical web application still looks something like this:</p>
<pre><code class="language-plaintext">Database
      ↓
Backend Models
      ↓
REST/GraphQL DTOs
      ↓
JSON
      ↓
Frontend Interfaces
</code></pre>
<p>Every layer defines the same data again.</p>
<p>Customer.</p>
<p>Order.</p>
<p>Invoice.</p>
<p>User.</p>
<p>Product.</p>
<p>Five different definitions.</p>
<p>Five opportunities for drift.</p>
<p>Eventually, someone changes the backend without updating the frontend.</p>
<p>The application still compiles.</p>
<p>It even deploys.</p>
<p>Then production breaks.</p>
<p>Sound familiar?</p>
<hr />
<h2>The Real Problem Isn't JavaScript</h2>
<p>Most runtime bugs today aren't caused by JavaScript itself.</p>
<p>They're caused by <strong>inconsistent contracts between services</strong>.</p>
<p>Examples include:</p>
<ul>
<li><p>renamed fields</p>
</li>
<li><p>nullable values</p>
</li>
<li><p>missing properties</p>
</li>
<li><p>incorrect enums</p>
</li>
<li><p>outdated API documentation</p>
</li>
<li><p>mismatched DTOs</p>
</li>
</ul>
<p>These issues rarely appear during development.</p>
<p>They appear after deployment.</p>
<p>And they're surprisingly expensive to fix.</p>
<hr />
<h2>What End-to-End Type Safety Really Means</h2>
<p>Instead of manually defining models everywhere, a single source of truth generates types for the entire application.</p>
<pre><code class="language-plaintext">Database Schema
        ↓
ORM Types
        ↓
API Contracts
        ↓
Server Logic
        ↓
Frontend Components
        ↓
Forms
        ↓
Validation
</code></pre>
<p>One change.</p>
<p>Every layer updates.</p>
<p>The compiler immediately highlights everything that needs attention.</p>
<p>No guessing.</p>
<p>No runtime surprises.</p>
<hr />
<h2>The Rise of Type-Driven Development</h2>
<p>Modern teams increasingly design APIs around types rather than documentation.</p>
<p>Instead of writing documentation first, they define contracts.</p>
<p>The compiler becomes the documentation.</p>
<p>Developers receive:</p>
<ul>
<li><p>autocomplete</p>
</li>
<li><p>compile-time validation</p>
</li>
<li><p>intelligent refactoring</p>
</li>
<li><p>safer code reviews</p>
</li>
</ul>
<p>Types evolve into executable architecture.</p>
<hr />
<h2>AI Makes Type Safety Even More Important</h2>
<p>One unexpected trend in 2026:</p>
<p>AI coding assistants generate code incredibly fast.</p>
<p>They also generate incorrect assumptions incredibly fast.</p>
<p>Without strong types, AI can produce:</p>
<ul>
<li><p>wrong API calls</p>
</li>
<li><p>incorrect payloads</p>
</li>
<li><p>invalid object structures</p>
</li>
<li><p>missing properties</p>
</li>
<li><p>incompatible function signatures</p>
</li>
</ul>
<p>The code may look convincing.</p>
<p>It may even pass a superficial review.</p>
<p>But it fails at runtime.</p>
<p>Strong typing provides immediate feedback, allowing both developers and AI assistants to catch errors before they reach production.</p>
<p>In many teams, the TypeScript compiler has effectively become the first AI reviewer.</p>
<hr />
<h2>The Modern Full-Stack TypeScript Stack</h2>
<p>Today's leading TypeScript teams commonly combine tools that share types across the stack:</p>
<ul>
<li><p>TypeScript 6.x</p>
</li>
<li><p>Node.js</p>
</li>
<li><p>React or Next.js</p>
</li>
<li><p>tRPC or GraphQL</p>
</li>
<li><p>Prisma or Drizzle ORM</p>
</li>
<li><p>Zod for runtime validation</p>
</li>
<li><p>OpenAPI code generation</p>
</li>
<li><p>TanStack Query</p>
</li>
<li><p>PostgreSQL</p>
</li>
</ul>
<p>The exact tools matter less than the underlying principle:</p>
<p><strong>Every layer speaks the same language.</strong></p>
<hr />
<h2>Runtime Validation Still Matters</h2>
<p>TypeScript only protects you during development.</p>
<p>Users don't compile your code.</p>
<p>Browsers don't compile your code.</p>
<p>External APIs don't compile your code.</p>
<p>That's why successful teams combine static types with runtime validation.</p>
<p>Libraries like Zod ensure that incoming data matches the expected contract before it enters the application.</p>
<p>Type safety and runtime validation complement each other—they are not interchangeable.</p>
<hr />
<h2>Refactoring Becomes a Superpower</h2>
<p>Consider renaming a field across a large codebase.</p>
<p>Without end-to-end types:</p>
<ul>
<li><p>Search.</p>
</li>
<li><p>Replace.</p>
</li>
<li><p>Hope.</p>
</li>
<li><p>Test.</p>
</li>
<li><p>Fix production.</p>
</li>
</ul>
<p>With end-to-end types:</p>
<ul>
<li><p>Rename once.</p>
</li>
<li><p>Let the compiler identify every affected location.</p>
</li>
<li><p>Resolve the highlighted issues.</p>
</li>
<li><p>Ship with confidence.</p>
</li>
</ul>
<p>As applications grow, this difference compounds significantly.</p>
<hr />
<h2>Better APIs, Better Teams</h2>
<p>Shared types offer benefits beyond code quality.</p>
<p>They improve collaboration.</p>
<p>Backend engineers no longer wonder what the frontend expects.</p>
<p>Frontend developers no longer guess the shape of API responses.</p>
<p>QA gains clearer contracts.</p>
<p>Technical writers can generate documentation automatically.</p>
<p>Even new team members become productive more quickly because the codebase becomes easier to understand.</p>
<hr />
<h2>Type Safety Is Becoming Infrastructure</h2>
<p>In 2026, type safety isn't just a language feature.</p>
<p>It's an architectural decision.</p>
<p>Just as version control, automated testing, CI/CD, and observability became standard engineering practices, end-to-end type safety is rapidly joining that list.</p>
<p>Organizations adopting it report:</p>
<ul>
<li><p>fewer integration bugs</p>
</li>
<li><p>faster feature delivery</p>
</li>
<li><p>more reliable AI-assisted development</p>
</li>
<li><p>safer large-scale refactoring</p>
</li>
<li><p>greater developer confidence</p>
</li>
</ul>
<p>The investment pays dividends throughout the software lifecycle.</p>
<hr />
<h2>Final Thoughts</h2>
<p>TypeScript has evolved far beyond "adding types to JavaScript."</p>
<p>It now serves as the contract connecting every layer of modern software systems.</p>
<p>The highest-performing engineering teams aren't using TypeScript simply because it's popular.</p>
<p>They're using it because shared types reduce friction, eliminate entire classes of bugs, and make both humans and AI more effective collaborators.</p>
<p>As software systems become increasingly distributed—and AI writes a growing percentage of production code—end-to-end type safety is no longer a luxury.</p>
<p>It's becoming a competitive advantage.</p>
<p><strong>How is your team ensuring end-to-end type safety today? Are you sharing types across the stack, generating contracts automatically, or still maintaining separate models for every layer? I'd love to hear what's working for you.</strong></p>
]]></content:encoded></item><item><title><![CDATA[10 Database Indexing Patterns That Will Instantly Speed Up Your Queries]]></title><description><![CDATA["The fastest query isn't the one with the most CPU—it's the one that reads the least data."
I've reviewed hundreds of SQL queries over the years, and one pattern appears repeatedly:
Developers spend d]]></description><link>https://billliao.hashnode.dev/10-database-indexing-patterns-that-will-instantly-speed-up-your-queries</link><guid isPermaLink="true">https://billliao.hashnode.dev/10-database-indexing-patterns-that-will-instantly-speed-up-your-queries</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:04:52 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/5adfe4ad-5b12-4802-b827-87573148e386.png" alt="" style="display:block;margin:0 auto" />

<p><em>"The fastest query isn't the one with the most CPU—it's the one that reads the least data."</em></p>
<p>I've reviewed hundreds of SQL queries over the years, and one pattern appears repeatedly:</p>
<p>Developers spend days optimizing application code while ignoring the database index that could reduce query time from <strong>8 seconds to 8 milliseconds</strong>.</p>
<p>Indexes are one of the highest ROI optimizations in software engineering—but only when designed correctly.</p>
<p>Here are <strong>10 database indexing patterns</strong> every backend engineer should know.</p>
<hr />
<h2>1. Covering Index</h2>
<p>A covering index contains <strong>all the columns</strong> needed by a query.</p>
<p>Instead of:</p>
<ul>
<li><p>Reading the index</p>
</li>
<li><p>Jumping back to the table</p>
</li>
<li><p>Reading the row</p>
</li>
</ul>
<p>The database answers the query directly from the index.</p>
<h3>Example</h3>
<pre><code class="language-plaintext">SELECT first_name, last_name
FROM users
WHERE country = 'UK';
</code></pre>
<p>Instead of:</p>
<pre><code class="language-plaintext">INDEX(country)
</code></pre>
<p>Create:</p>
<pre><code class="language-plaintext">INDEX(country, first_name, last_name)
</code></pre>
<p><strong>Benefits</strong></p>
<p>✅ Eliminates table lookups</p>
<p>✅ Dramatically reduces I/O</p>
<p>✅ Perfect for high-frequency read APIs</p>
<hr />
<h2>2. Composite Index</h2>
<p>Most queries filter by multiple columns.</p>
<p>Instead of several single-column indexes:</p>
<pre><code class="language-plaintext">INDEX(status)
INDEX(created_at)
</code></pre>
<p>Use:</p>
<pre><code class="language-plaintext">INDEX(status, created_at)
</code></pre>
<p>For:</p>
<pre><code class="language-plaintext">WHERE status='ACTIVE'
AND created_at &gt; NOW()-INTERVAL 30 DAY
</code></pre>
<p>The database performs one efficient index scan instead of combining multiple indexes.</p>
<hr />
<h2>3. Left-Most Prefix Rule</h2>
<p>Composite indexes only work efficiently from the <strong>left side</strong>.</p>
<p>Example:</p>
<pre><code class="language-plaintext">INDEX(country, city, zipcode)
</code></pre>
<p>Efficient:</p>
<pre><code class="language-plaintext">WHERE country=?
WHERE country=? AND city=?
WHERE country=? AND city=? AND zipcode=?
</code></pre>
<p>Not efficient:</p>
<pre><code class="language-plaintext">WHERE city=?
WHERE zipcode=?
</code></pre>
<p>Column order matters more than many developers realize.</p>
<hr />
<h2>4. Partial (Filtered) Index</h2>
<p>Sometimes only a small percentage of rows are queried.</p>
<p>Example:</p>
<pre><code class="language-plaintext">WHERE status='ACTIVE'
</code></pre>
<p>Instead of indexing the entire table:</p>
<pre><code class="language-plaintext">CREATE INDEX idx_active
ON users(last_login)
WHERE status='ACTIVE';
</code></pre>
<p>Advantages:</p>
<ul>
<li><p>Smaller index</p>
</li>
<li><p>Faster updates</p>
</li>
<li><p>Less storage</p>
</li>
<li><p>Faster reads</p>
</li>
</ul>
<p>Especially useful in PostgreSQL.</p>
<hr />
<h2>5. Unique Index</h2>
<p>Need uniqueness?</p>
<p>Don't check it in application code.</p>
<p>Use:</p>
<pre><code class="language-plaintext">CREATE UNIQUE INDEX
ON users(email);
</code></pre>
<p>Benefits:</p>
<ul>
<li><p>Guarantees data integrity</p>
</li>
<li><p>Prevents race conditions</p>
</li>
<li><p>Accelerates lookups</p>
</li>
</ul>
<p>One feature. Two benefits.</p>
<hr />
<h2>6. Descending Index</h2>
<p>Many applications retrieve recent records.</p>
<pre><code class="language-plaintext">ORDER BY created_at DESC
LIMIT 20
</code></pre>
<p>Instead of sorting millions of rows:</p>
<pre><code class="language-plaintext">INDEX(created_at DESC)
</code></pre>
<p>The database reads directly in reverse order.</p>
<p>Ideal for:</p>
<ul>
<li><p>Dashboards</p>
</li>
<li><p>Activity feeds</p>
</li>
<li><p>Event logs</p>
</li>
<li><p>Notifications</p>
</li>
</ul>
<hr />
<h2>7. Functional (Expression) Index</h2>
<p>Queries often apply functions:</p>
<pre><code class="language-plaintext">WHERE LOWER(email)=?
</code></pre>
<p>Without an expression index:</p>
<p>Every row must be processed.</p>
<p>Instead:</p>
<pre><code class="language-plaintext">CREATE INDEX idx_email
ON users(LOWER(email));
</code></pre>
<p>Perfect for:</p>
<ul>
<li><p>Case-insensitive search</p>
</li>
<li><p>Date extraction</p>
</li>
<li><p>JSON fields</p>
</li>
<li><p>Calculated values</p>
</li>
</ul>
<hr />
<h2>8. Clustered Index</h2>
<p>A clustered index determines the physical order of data.</p>
<p>Rows with nearby keys are stored together.</p>
<p>Ideal for:</p>
<ul>
<li><p>Range scans</p>
</li>
<li><p>Sequential reads</p>
</li>
<li><p>Time-series data</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-plaintext">WHERE created_at
BETWEEN ...
</code></pre>
<p>SQL Server and InnoDB heavily benefit from good clustered index design.</p>
<hr />
<h2>9. Hash Index</h2>
<p>Hash indexes excel at exact lookups.</p>
<p>Perfect for:</p>
<pre><code class="language-plaintext">WHERE email=?
</code></pre>
<p>Not suitable for:</p>
<pre><code class="language-plaintext">&gt;
&lt;
BETWEEN
ORDER BY
</code></pre>
<p>Use when equality lookups dominate.</p>
<p>(PostgreSQL supports hash indexes, although B-tree remains the default for most workloads.)</p>
<hr />
<h2>10. BRIN Index</h2>
<p>Massive tables?</p>
<p>Think billions of rows.</p>
<p>A traditional B-tree may become enormous.</p>
<p>PostgreSQL's BRIN (Block Range Index):</p>
<ul>
<li><p>Tiny size</p>
</li>
<li><p>Extremely fast to build</p>
</li>
<li><p>Excellent for append-only tables</p>
</li>
</ul>
<p>Ideal for:</p>
<ul>
<li><p>Logs</p>
</li>
<li><p>Sensor data</p>
</li>
<li><p>Financial transactions</p>
</li>
<li><p>Event streams</p>
</li>
</ul>
<p>A BRIN index can be only a tiny fraction of the size of an equivalent B-tree.</p>
<hr />
<h2>Common Indexing Mistakes</h2>
<p>❌ Index every column</p>
<p>More indexes = slower INSERT/UPDATE/DELETE.</p>
<hr />
<p>❌ Wrong column order</p>
<p>Always place:</p>
<ul>
<li><p>High selectivity first</p>
</li>
<li><p>Frequently filtered columns first</p>
</li>
</ul>
<hr />
<p>❌ Ignore execution plans</p>
<p>Always inspect:</p>
<pre><code class="language-plaintext">EXPLAIN
</code></pre>
<p>or</p>
<pre><code class="language-plaintext">EXPLAIN ANALYZE
</code></pre>
<p>The optimizer tells you exactly what it's doing.</p>
<hr />
<p>❌ Duplicate indexes</p>
<p>Many databases contain:</p>
<pre><code class="language-plaintext">INDEX(a)

INDEX(a,b)

INDEX(a,b,c)
</code></pre>
<p>Often one well-designed composite index is enough.</p>
<hr />
<h2>Choosing the Right Index</h2>
<p>Query PatternRecommended IndexEquality lookupHash / B-treeRange searchB-treeORDER BYDescending IndexMultiple filtersComposite IndexFrequent readsCovering IndexCase-insensitive searchFunctional IndexActive subsetPartial IndexHuge append-only tableBRINUnique valuesUnique IndexTime-series dataClustered Index</p>
<hr />
<h2>Final Thoughts</h2>
<p>Database performance is rarely about writing "clever" SQL.</p>
<p>It's about helping the optimizer find data with the fewest possible disk reads.</p>
<p>The best engineers don't just optimize code—they optimize how data is accessed.</p>
<p>Before scaling your infrastructure or adding more CPU and memory, ask yourself one question:</p>
<p><strong>Could the right index solve this problem instead?</strong></p>
<p>You might be surprised how often the answer is <strong>yes</strong>.</p>
<hr />
<p><strong>What indexing technique has delivered the biggest performance improvement in your projects? Share your experience in the comments!</strong></p>
]]></content:encoded></item><item><title><![CDATA[Designing Systems for AI Agents]]></title><description><![CDATA[We spent decades designing systems for humans.
What happens when your primary users become AI agents instead?
Most software isn't ready.
For years, software architecture has revolved around one assump]]></description><link>https://billliao.hashnode.dev/designing-systems-for-ai-agents</link><guid isPermaLink="true">https://billliao.hashnode.dev/designing-systems-for-ai-agents</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:04:02 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/334b0a3f-de25-43f8-b99f-36e5fb80d2a8.png" alt="" style="display:block;margin:0 auto" />

<p>We spent decades designing systems for humans.</p>
<p>What happens when your primary users become AI agents instead?</p>
<p>Most software isn't ready.</p>
<p>For years, software architecture has revolved around one assumption:</p>
<p><strong>Humans make decisions. Software executes them.</strong></p>
<p>A user clicks a button.</p>
<p>A backend validates the request.</p>
<p>A database stores the result.</p>
<p>An API returns a response.</p>
<p>The software is deterministic, predictable, and largely stateless between interactions.</p>
<p>AI agents completely change that model.</p>
<p>Instead of following predefined workflows, agents <strong>reason, plan, remember, collaborate, call tools, recover from failures, and continuously adapt.</strong></p>
<p>That means we're no longer designing applications.</p>
<p>We're designing <strong>ecosystems where autonomous software entities operate.</strong></p>
<hr />
<h3>The Architecture Shift</h3>
<p>Traditional architecture asks:</p>
<ul>
<li><p>How do users interact with the system?</p>
</li>
<li><p>How should services communicate?</p>
</li>
<li><p>How do we scale traffic?</p>
</li>
</ul>
<p>Agentic architecture asks different questions:</p>
<ul>
<li><p>How does an agent discover capabilities?</p>
</li>
<li><p>How does it decide which tool to use?</p>
</li>
<li><p>How does it recover from uncertainty?</p>
</li>
<li><p>How do multiple agents collaborate safely?</p>
</li>
<li><p>How do we observe reasoning that isn't deterministic?</p>
</li>
</ul>
<p>Those questions didn't exist five years ago.</p>
<p>Today, they're becoming the most important design decisions.</p>
<hr />
<h2>Think Beyond APIs</h2>
<p>Traditional systems expose APIs.</p>
<p>Agent systems expose <strong>capabilities.</strong></p>
<p>Instead of:</p>
<pre><code class="language-plaintext">POST /orders
</code></pre>
<p>An agent thinks:</p>
<blockquote>
<p>"I need to create an order."</p>
</blockquote>
<p>Then discovers available tools:</p>
<ul>
<li><p>CreateOrder</p>
</li>
<li><p>SearchInventory</p>
</li>
<li><p>EstimateShipping</p>
</li>
<li><p>ValidatePayment</p>
</li>
<li><p>NotifyCustomer</p>
</li>
</ul>
<p>The interface becomes semantic instead of procedural.</p>
<p>Instead of calling endpoints directly, agents choose capabilities based on context.</p>
<p>This requires APIs to become:</p>
<ul>
<li><p>self-describing</p>
</li>
<li><p>discoverable</p>
</li>
<li><p>machine-readable</p>
</li>
<li><p>version-aware</p>
</li>
</ul>
<p>The future isn't API-first.</p>
<p>It's <strong>capability-first.</strong></p>
<hr />
<h2>Context Becomes Infrastructure</h2>
<p>Traditional applications treat context as temporary.</p>
<p>Agents treat context as memory.</p>
<p>An agent may need to remember:</p>
<ul>
<li><p>previous conversations</p>
</li>
<li><p>earlier decisions</p>
</li>
<li><p>business constraints</p>
</li>
<li><p>user preferences</p>
</li>
<li><p>failed attempts</p>
</li>
<li><p>retrieved documents</p>
</li>
<li><p>external knowledge</p>
</li>
</ul>
<p>Suddenly, context is no longer just prompt engineering.</p>
<p>It's infrastructure.</p>
<p>Modern systems need multiple layers of memory:</p>
<h3>Short-term</h3>
<p>Current conversation.</p>
<h3>Working memory</h3>
<p>Active task state.</p>
<h3>Long-term memory</h3>
<p>Persistent knowledge.</p>
<h3>Organizational knowledge</h3>
<p>Documentation, policies, architecture decisions, code repositories, tickets, and databases.</p>
<p>Without effective context management, even the smartest LLM behaves like someone suffering from amnesia.</p>
<hr />
<h2>Every Tool Is a Distributed System</h2>
<p>When an AI agent calls a tool, it isn't making a simple API request.</p>
<p>It's orchestrating distributed systems.</p>
<p>One task may involve:</p>
<ul>
<li><p>ERP</p>
</li>
<li><p>CRM</p>
</li>
<li><p>payment gateways</p>
</li>
<li><p>email services</p>
</li>
<li><p>Slack</p>
</li>
<li><p>GitHub</p>
</li>
<li><p>databases</p>
</li>
<li><p>cloud storage</p>
</li>
<li><p>vector search</p>
</li>
</ul>
<p>One reasoning step may trigger dozens of network calls.</p>
<p>Failures become normal.</p>
<p>Timeouts become expected.</p>
<p>Partial success becomes common.</p>
<p>Agent platforms therefore need:</p>
<ul>
<li><p>retries</p>
</li>
<li><p>idempotency</p>
</li>
<li><p>circuit breakers</p>
</li>
<li><p>compensation</p>
</li>
<li><p>rollback strategies</p>
</li>
<li><p>observability</p>
</li>
</ul>
<p>Ironically, many "AI problems" are actually classic distributed systems problems.</p>
<hr />
<h2>Trust Requires Explainability</h2>
<p>Traditional applications rarely explain why they made a decision.</p>
<p>Agents must.</p>
<p>Imagine an AI approving a $500,000 loan.</p>
<p>Would you trust:</p>
<blockquote>
<p>"The model said yes."</p>
</blockquote>
<p>Probably not.</p>
<p>Instead, organizations need:</p>
<ul>
<li><p>evidence used</p>
</li>
<li><p>confidence scores</p>
</li>
<li><p>source citations</p>
</li>
<li><p>reasoning summaries</p>
</li>
<li><p>policy references</p>
</li>
<li><p>decision history</p>
</li>
</ul>
<p>Explainability isn't a nice-to-have.</p>
<p>It's a production requirement.</p>
<hr />
<h2>Permissions Must Become Dynamic</h2>
<p>Traditional RBAC assumes humans.</p>
<p>Agents are different.</p>
<p>They may:</p>
<ul>
<li><p>act on behalf of users</p>
</li>
<li><p>delegate tasks</p>
</li>
<li><p>collaborate with other agents</p>
</li>
<li><p>temporarily elevate permissions</p>
</li>
<li><p>call external systems</p>
</li>
</ul>
<p>Permissions become contextual.</p>
<p>Questions evolve into:</p>
<p>Can this agent:</p>
<ul>
<li><p>read this document?</p>
</li>
<li><p>execute this tool?</p>
</li>
<li><p>spend this budget?</p>
</li>
<li><p>modify production?</p>
</li>
<li><p>approve payments?</p>
</li>
</ul>
<p>Identity, authorization, and auditing become central architecture concerns.</p>
<hr />
<h2>Multi-Agent Systems Need Coordination</h2>
<p>One agent rarely solves everything.</p>
<p>Imagine an enterprise platform:</p>
<p>Planner Agent</p>
<p>↓</p>
<p>Research Agent</p>
<p>↓</p>
<p>Coding Agent</p>
<p>↓</p>
<p>Testing Agent</p>
<p>↓</p>
<p>Deployment Agent</p>
<p>↓</p>
<p>Monitoring Agent</p>
<p>↓</p>
<p>Incident Response Agent</p>
<p>Now architecture looks less like microservices and more like an intelligent workforce.</p>
<p>The challenge isn't creating agents.</p>
<p>The challenge is coordinating them.</p>
<p>That requires:</p>
<ul>
<li><p>task routing</p>
</li>
<li><p>shared memory</p>
</li>
<li><p>conflict resolution</p>
</li>
<li><p>event orchestration</p>
</li>
<li><p>workflow recovery</p>
</li>
<li><p>governance</p>
</li>
</ul>
<p>We're moving from Service-Oriented Architecture to <strong>Agent-Oriented Architecture.</strong></p>
<hr />
<h2>Observability Gets Harder</h2>
<p>Traditional monitoring tracks:</p>
<ul>
<li><p>latency</p>
</li>
<li><p>CPU</p>
</li>
<li><p>memory</p>
</li>
<li><p>requests</p>
</li>
<li><p>errors</p>
</li>
</ul>
<p>Agent systems require much more.</p>
<p>We need visibility into:</p>
<ul>
<li><p>reasoning paths</p>
</li>
<li><p>tool selection</p>
</li>
<li><p>prompt versions</p>
</li>
<li><p>retrieved knowledge</p>
</li>
<li><p>token usage</p>
</li>
<li><p>decision chains</p>
</li>
<li><p>hallucination rates</p>
</li>
<li><p>recovery attempts</p>
</li>
</ul>
<p>Logs become conversations.</p>
<p>Tracing becomes reasoning graphs.</p>
<p>Dashboards become execution timelines.</p>
<hr />
<h2>Designing for Failure</h2>
<p>Agents will make mistakes.</p>
<p>That's guaranteed.</p>
<p>Good systems assume:</p>
<ul>
<li><p>hallucinations happen</p>
</li>
<li><p>tools fail</p>
</li>
<li><p>APIs change</p>
</li>
<li><p>context is incomplete</p>
</li>
<li><p>users give ambiguous instructions</p>
</li>
<li><p>models disagree</p>
</li>
</ul>
<p>Production-ready agent systems include:</p>
<p>✅ Human approval checkpoints</p>
<p>✅ Safe fallback workflows</p>
<p>✅ Retry strategies</p>
<p>✅ Rollback mechanisms</p>
<p>✅ Guardrails</p>
<p>✅ Continuous evaluation</p>
<p>Reliability isn't achieved by making AI perfect.</p>
<p>It's achieved by making failures recoverable.</p>
<hr />
<h2>What Architects Should Focus On</h2>
<p>Instead of asking:</p>
<blockquote>
<p>Which LLM should we use?</p>
</blockquote>
<p>Start asking:</p>
<ul>
<li><p>How will agents discover capabilities?</p>
</li>
<li><p>Where does memory live?</p>
</li>
<li><p>How is context managed?</p>
</li>
<li><p>How do agents collaborate?</p>
</li>
<li><p>How are permissions enforced?</p>
</li>
<li><p>How are decisions audited?</p>
</li>
<li><p>How do we observe reasoning?</p>
</li>
<li><p>What happens when an agent is wrong?</p>
</li>
</ul>
<p>Those questions will shape the next generation of software architecture.</p>
<hr />
<h2>Final Thoughts</h2>
<p>The biggest misconception about AI is that it's just another feature.</p>
<p>It isn't.</p>
<p>It's a new execution model.</p>
<p>Over the past two decades, we've evolved from monoliths to microservices, embraced cloud-native platforms, adopted event-driven architectures, and built resilient distributed systems.</p>
<p>Now we're entering another transformation.</p>
<p>We're designing systems where software doesn't simply execute instructions—it <strong>makes decisions, coordinates actions, and collaborates autonomously.</strong></p>
<p>The architects who succeed won't be the ones who choose the best model.</p>
<p>They'll be the ones who build the best systems <strong>around</strong> the model.</p>
<p>Because in the age of AI agents, architecture is no longer just about software.</p>
<p>It's about designing environments where intelligence can operate safely, reliably, and at scale.</p>
<hr />
<p><strong>What architectural principle do you think will matter most in the era of AI agents—context, memory, observability, governance, or something else? I'd love to hear your perspective.</strong></p>
]]></content:encoded></item><item><title><![CDATA[The Database Design Checklist I Use Before Going to Production]]></title><description><![CDATA["Good software rarely fails because of bad code. It often fails because of bad data design."
After more than 30 years of building enterprise systems, I've learned one lesson repeatedly:
Most productio]]></description><link>https://billliao.hashnode.dev/the-database-design-checklist-i-use-before-going-to-production</link><guid isPermaLink="true">https://billliao.hashnode.dev/the-database-design-checklist-i-use-before-going-to-production</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:03:09 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/ad890774-4ade-41ca-a30f-898fc2364f7b.png" alt="" style="display:block;margin:0 auto" />

<p><em>"Good software rarely fails because of bad code. It often fails because of bad data design."</em></p>
<p>After more than 30 years of building enterprise systems, I've learned one lesson repeatedly:</p>
<p><strong>Most production incidents start long before deployment.</strong></p>
<p>They begin when someone designs a database without thinking about scalability, consistency, security, or future change.</p>
<p>I've reviewed hundreds of database schemas over the years, and before any system reaches production, I always walk through the same checklist.</p>
<p>It doesn't guarantee perfection.</p>
<p>But it dramatically reduces expensive mistakes.</p>
<p>Here is the database design checklist I rely on.</p>
<hr />
<h2>1. Does Every Table Have a Clear Purpose?</h2>
<p>Every table should represent exactly one business concept.</p>
<p>If a table stores customers, invoices, permissions, audit logs, and configuration together, you've already created technical debt.</p>
<p>Ask yourself:</p>
<ul>
<li><p>Can I explain this table in one sentence?</p>
</li>
<li><p>Does it represent one business entity?</p>
</li>
<li><p>Would another engineer immediately understand its purpose?</p>
</li>
</ul>
<p>If not, redesign it.</p>
<hr />
<h2>2. Are Primary Keys Chosen Correctly?</h2>
<p>Primary keys affect nearly everything:</p>
<ul>
<li><p>Performance</p>
</li>
<li><p>Storage</p>
</li>
<li><p>Replication</p>
</li>
<li><p>Index size</p>
</li>
<li><p>API design</p>
</li>
</ul>
<p>Questions I ask:</p>
<ul>
<li><p>Should this use UUID or BIGINT?</p>
</li>
<li><p>Will IDs be generated centrally or distributed?</p>
</li>
<li><p>Will the key ever need to change?</p>
</li>
</ul>
<p>Changing primary keys after production is painful.</p>
<p>Choose carefully.</p>
<hr />
<h2>3. Are Relationships Explicit?</h2>
<p>Relationships should never exist only in developers' heads.</p>
<p>Verify:</p>
<ul>
<li><p>Foreign keys</p>
</li>
<li><p>Cascade rules</p>
</li>
<li><p>Optional vs mandatory relationships</p>
</li>
<li><p>Many-to-many junction tables</p>
</li>
</ul>
<p>Hidden relationships eventually create inconsistent data.</p>
<hr />
<h2>4. Is the Database Properly Normalized?</h2>
<p>Normalization prevents duplication.</p>
<p>But over-normalization can destroy performance.</p>
<p>I usually aim for:</p>
<ul>
<li><p>Third Normal Form (3NF)</p>
</li>
<li><p>Denormalize only when measurements justify it</p>
</li>
<li><p>Never denormalize because "it feels faster"</p>
</li>
</ul>
<p>Optimization without evidence is guesswork.</p>
<hr />
<h2>5. Have Indexes Been Designed Intentionally?</h2>
<p>Indexes are not free.</p>
<p>Every index speeds up reads but slows:</p>
<ul>
<li><p>Inserts</p>
</li>
<li><p>Updates</p>
</li>
<li><p>Deletes</p>
</li>
<li><p>Storage</p>
</li>
</ul>
<p>For every index I ask:</p>
<ul>
<li><p>Which query uses it?</p>
</li>
<li><p>Is it selective?</p>
</li>
<li><p>Can two indexes become one composite index?</p>
</li>
<li><p>Is this index actually used?</p>
</li>
</ul>
<p>Unused indexes quietly waste resources.</p>
<hr />
<h2>6. Can the Database Scale?</h2>
<p>Today's 10,000 rows may become tomorrow's 100 million.</p>
<p>Consider:</p>
<ul>
<li><p>Partitioning strategy</p>
</li>
<li><p>Archiving</p>
</li>
<li><p>Read replicas</p>
</li>
<li><p>Hotspot prevention</p>
</li>
<li><p>Sharding possibilities</p>
</li>
</ul>
<p>A scalable schema is much cheaper than a late migration.</p>
<hr />
<h2>7. Are Transactions Clearly Defined?</h2>
<p>Every transaction should have clear boundaries.</p>
<p>Questions include:</p>
<ul>
<li><p>Which operations must be atomic?</p>
</li>
<li><p>What happens during rollback?</p>
</li>
<li><p>Could deadlocks occur?</p>
</li>
<li><p>Is optimistic locking sufficient?</p>
</li>
</ul>
<p>Concurrency bugs are among the hardest production issues to diagnose.</p>
<hr />
<h2>8. Have You Planned for Auditing?</h2>
<p>Production systems eventually require answers to questions like:</p>
<ul>
<li><p>Who changed this?</p>
</li>
<li><p>When?</p>
</li>
<li><p>What was the previous value?</p>
</li>
<li><p>Why was it modified?</p>
</li>
</ul>
<p>Design for auditing early.</p>
<p>Adding audit history later is surprisingly difficult.</p>
<hr />
<h2>9. Are Soft Deletes Really Necessary?</h2>
<p>Many systems add an is_deleted column by default.</p>
<p>Sometimes that's correct.</p>
<p>Sometimes it creates years of unnecessary complexity.</p>
<p>Ask:</p>
<ul>
<li><p>Is recovery actually required?</p>
</li>
<li><p>Would archive tables work better?</p>
</li>
<li><p>How will indexes behave?</p>
</li>
<li><p>Will every query remember to filter deleted records?</p>
</li>
</ul>
<p>Soft delete is a business decision—not a default.</p>
<hr />
<h2>10. Have Sensitive Data Been Protected?</h2>
<p>Security should be part of the schema.</p>
<p>Review:</p>
<ul>
<li><p>Encryption at rest</p>
</li>
<li><p>Field-level encryption</p>
</li>
<li><p>Personally identifiable information (PII)</p>
</li>
<li><p>Password storage</p>
</li>
<li><p>Tokenization</p>
</li>
<li><p>Data masking</p>
</li>
<li><p>GDPR or regulatory compliance</p>
</li>
</ul>
<p>Security isn't an add-on.</p>
<p>It's part of database design.</p>
<hr />
<h2>11. Is Every Naming Convention Consistent?</h2>
<p>Consistency reduces cognitive load.</p>
<p>Choose standards for:</p>
<ul>
<li><p>Table names</p>
</li>
<li><p>Column names</p>
</li>
<li><p>Constraints</p>
</li>
<li><p>Indexes</p>
</li>
<li><p>Foreign keys</p>
</li>
<li><p>Stored procedures</p>
</li>
</ul>
<p>Good naming makes databases self-documenting.</p>
<hr />
<h2>12. Are NULL Values Clearly Defined?</h2>
<p>NULL often hides ambiguity.</p>
<p>For every nullable column ask:</p>
<p>"What does NULL actually mean?"</p>
<p>Unknown?</p>
<p>Not applicable?</p>
<p>Not yet provided?</p>
<p>These are different business meanings.</p>
<p>Treat them differently.</p>
<hr />
<h2>13. Have Default Values Been Reviewed?</h2>
<p>Poor defaults create bad data.</p>
<p>Avoid defaults that hide missing information.</p>
<p>Instead, ensure defaults genuinely represent sensible business behavior.</p>
<hr />
<h2>14. Is the Schema Ready for Future Changes?</h2>
<p>Requirements always evolve.</p>
<p>Ask yourself:</p>
<ul>
<li><p>Can new statuses be added?</p>
</li>
<li><p>Can new payment methods appear?</p>
</li>
<li><p>Can new countries be supported?</p>
</li>
<li><p>Can new product types be introduced?</p>
</li>
</ul>
<p>Schemas that evolve gracefully survive much longer.</p>
<hr />
<h2>15. Has Performance Been Tested with Realistic Data?</h2>
<p>Many schemas perform perfectly with:</p>
<ul>
<li><p>100 rows</p>
</li>
<li><p>1,000 rows</p>
</li>
<li><p>Empty indexes</p>
</li>
</ul>
<p>Reality looks different.</p>
<p>Always test with production-scale datasets.</p>
<p>Measure:</p>
<ul>
<li><p>Query latency</p>
</li>
<li><p>Lock contention</p>
</li>
<li><p>Index usage</p>
</li>
<li><p>Execution plans</p>
</li>
<li><p>Storage growth</p>
</li>
</ul>
<p>Never trust theoretical performance.</p>
<hr />
<h2>16. Is Backup and Recovery Verified?</h2>
<p>Backups are useless until restored.</p>
<p>Before production confirm:</p>
<ul>
<li><p>Recovery procedures work</p>
</li>
<li><p>Recovery time objectives (RTO) are achievable</p>
</li>
<li><p>Recovery point objectives (RPO) meet business needs</p>
</li>
<li><p>Point-in-time recovery is tested</p>
</li>
</ul>
<p>The best backup is the one you've successfully restored.</p>
<hr />
<h2>17. Have Migration Scripts Been Reviewed?</h2>
<p>Production deployments depend on migrations.</p>
<p>Review every migration for:</p>
<ul>
<li><p>Rollback strategy</p>
</li>
<li><p>Long-running locks</p>
</li>
<li><p>Backward compatibility</p>
</li>
<li><p>Idempotency</p>
</li>
<li><p>Large-table impact</p>
</li>
</ul>
<p>Schema changes deserve the same code review as application code.</p>
<hr />
<h2>Final Thoughts</h2>
<p>Many engineers believe database design is just about creating tables.</p>
<p>It isn't.</p>
<p>It's about designing the foundation of your entire system.</p>
<p>Great schemas make applications simpler.</p>
<p>Poor schemas force developers to write increasingly complicated code forever.</p>
<p>Before every production release, I revisit this checklist.</p>
<p>It has helped me avoid countless outages, expensive redesigns, and late-night emergency fixes.</p>
<p><strong>The best database design is rarely the cleverest one.</strong></p>
<p><strong>It's the one that continues working five years after everyone has forgotten who designed it.</strong></p>
<hr />
<h3>What's on your production database checklist?</h3>
<p>Have you ever caught a critical issue just before deployment?</p>
<p>I'd love to hear the checks that have saved you from production disasters.</p>
]]></content:encoded></item><item><title><![CDATA[10 Database Design Patterns Every Senior Engineer Should Know]]></title><description><![CDATA[Most software engineers learn SQL.
Senior engineers learn database design.
The difference?
A junior engineer asks:

"Which database should we use?"

A senior engineer asks:

"How should data evolve, s]]></description><link>https://billliao.hashnode.dev/10-database-design-patterns-every-senior-engineer-should-know</link><guid isPermaLink="true">https://billliao.hashnode.dev/10-database-design-patterns-every-senior-engineer-should-know</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:02:04 GMT</pubDate><content:encoded><![CDATA[<p>Most software engineers learn SQL.</p>
<p>Senior engineers learn <strong>database design</strong>.</p>
<p>The difference?</p>
<p>A junior engineer asks:</p>
<blockquote>
<p><em>"Which database should we use?"</em></p>
</blockquote>
<p>A senior engineer asks:</p>
<blockquote>
<p><em>"How should data evolve, scale, and survive the next five years?"</em></p>
</blockquote>
<p>The database is often the biggest bottleneck—or the biggest competitive advantage—of a system. Great schema design reduces technical debt, improves scalability, and makes future features dramatically easier to build.</p>
<p>Here are <strong>10 database design patterns</strong> every senior engineer should understand.</p>
<hr />
<h2>1. Normalization</h2>
<p><strong>Purpose:</strong> Eliminate redundancy and maintain data consistency.</p>
<p>Instead of storing duplicate information, split data into logical tables connected by keys.</p>
<p>Example:</p>
<ul>
<li><p>Users</p>
</li>
<li><p>Orders</p>
</li>
<li><p>Products</p>
</li>
<li><p>OrderItems</p>
</li>
</ul>
<p>rather than placing everything into one giant table.</p>
<h3>Best for</h3>
<ul>
<li><p>Financial systems</p>
</li>
<li><p>ERP</p>
</li>
<li><p>CRM</p>
</li>
<li><p>Banking</p>
</li>
<li><p>Enterprise applications</p>
</li>
</ul>
<h3>Benefits</h3>
<ul>
<li><p>Consistent data</p>
</li>
<li><p>Smaller storage footprint</p>
</li>
<li><p>Easier updates</p>
</li>
</ul>
<h3>Trade-off</h3>
<p>Too much normalization increases JOIN complexity.</p>
<hr />
<h2>2. Denormalization</h2>
<p>Sometimes duplication is intentional.</p>
<p>Modern applications often duplicate data to improve read performance.</p>
<p>Example:</p>
<p>Instead of joining five tables for every request:</p>
<pre><code class="language-plaintext">Order
Customer
Address
Country
Currency
</code></pre>
<p>Store customer name and shipping address directly inside the Order record.</p>
<p>Yes, it creates redundancy.</p>
<p>But reads become dramatically faster.</p>
<h3>Best for</h3>
<ul>
<li><p>High-read systems</p>
</li>
<li><p>Analytics</p>
</li>
<li><p>Dashboards</p>
</li>
<li><p>Reporting</p>
</li>
</ul>
<h3>Trade-off</h3>
<p>More complex updates.</p>
<hr />
<h2>3. Soft Delete Pattern</h2>
<p>Never physically delete important data.</p>
<p>Instead:</p>
<pre><code class="language-plaintext">is_deleted = true
deleted_at = timestamp
deleted_by = user
</code></pre>
<p>Advantages:</p>
<ul>
<li><p>Easy recovery</p>
</li>
<li><p>Audit trail</p>
</li>
<li><p>Regulatory compliance</p>
</li>
<li><p>Prevent accidental data loss</p>
</li>
</ul>
<p>Used heavily in:</p>
<ul>
<li><p>SaaS</p>
</li>
<li><p>Healthcare</p>
</li>
<li><p>Finance</p>
</li>
<li><p>Enterprise platforms</p>
</li>
</ul>
<hr />
<h2>4. Audit Log Pattern</h2>
<p>Track every important change.</p>
<p>Instead of:</p>
<pre><code class="language-plaintext">Customer
</code></pre>
<p>Keep:</p>
<pre><code class="language-plaintext">Customer
CustomerHistory
</code></pre>
<p>Every update becomes an immutable history record.</p>
<p>Useful for:</p>
<ul>
<li><p>Debugging</p>
</li>
<li><p>Compliance</p>
</li>
<li><p>Security investigations</p>
</li>
<li><p>Data recovery</p>
</li>
</ul>
<hr />
<h2>5. Versioning Pattern</h2>
<p>Instead of updating rows:</p>
<pre><code class="language-plaintext">Document V1
Document V2
Document V3
</code></pre>
<p>Store multiple versions.</p>
<p>Perfect for:</p>
<ul>
<li><p>Wikis</p>
</li>
<li><p>Documents</p>
</li>
<li><p>AI prompts</p>
</li>
<li><p>Configuration management</p>
</li>
<li><p>Knowledge bases</p>
</li>
</ul>
<p>Git works because of versioning.</p>
<p>Your database can too.</p>
<hr />
<h2>6. Event Sourcing</h2>
<p>Instead of storing the latest state...</p>
<p>Store every event.</p>
<p>Example:</p>
<pre><code class="language-plaintext">Account Created

Money Deposited

Money Withdrawn

Interest Applied
</code></pre>
<p>Current balance is reconstructed from events.</p>
<p>Benefits:</p>
<ul>
<li><p>Complete history</p>
</li>
<li><p>Time travel</p>
</li>
<li><p>Replay capability</p>
</li>
<li><p>Debugging</p>
</li>
</ul>
<p>Common in:</p>
<ul>
<li><p>Banking</p>
</li>
<li><p>Trading</p>
</li>
<li><p>FinTech</p>
</li>
</ul>
<hr />
<h2>7. CQRS (Command Query Responsibility Segregation)</h2>
<p>Separate writes from reads.</p>
<p>Instead of one database handling everything:</p>
<pre><code class="language-plaintext">Write Database
↓

Event Bus
↓

Read Database
</code></pre>
<p>Reads can be optimized independently.</p>
<p>Advantages:</p>
<ul>
<li><p>Faster queries</p>
</li>
<li><p>Better scalability</p>
</li>
<li><p>Independent optimization</p>
</li>
</ul>
<p>Ideal when reads greatly exceed writes.</p>
<hr />
<h2>8. Sharding Pattern</h2>
<p>One database eventually becomes too large.</p>
<p>Split data horizontally.</p>
<p>Example:</p>
<pre><code class="language-plaintext">Shard 1
Customer 1–1M

Shard 2
Customer 1M–2M

Shard 3
Customer 2M–3M
</code></pre>
<p>Benefits:</p>
<ul>
<li><p>Horizontal scaling</p>
</li>
<li><p>Smaller indexes</p>
</li>
<li><p>Faster queries</p>
</li>
</ul>
<p>Challenges:</p>
<ul>
<li><p>Cross-shard joins</p>
</li>
<li><p>Transactions</p>
</li>
<li><p>Operational complexity</p>
</li>
</ul>
<hr />
<h2>9. Multi-Tenant Pattern</h2>
<p>One application.</p>
<p>Many customers.</p>
<p>Common approaches:</p>
<h3>Shared Database</h3>
<p>Cheapest.</p>
<pre><code class="language-plaintext">TenantId
</code></pre>
<p>exists in every table.</p>
<h3>Separate Schemas</h3>
<p>Better isolation.</p>
<h3>Separate Databases</h3>
<p>Maximum security.</p>
<p>More operational overhead.</p>
<p>Choosing the right model depends on:</p>
<ul>
<li><p>Security</p>
</li>
<li><p>Scale</p>
</li>
<li><p>Compliance</p>
</li>
<li><p>Cost</p>
</li>
</ul>
<hr />
<h2>10. Outbox Pattern</h2>
<p>Distributed transactions are hard.</p>
<p>Instead of updating a database and publishing a message simultaneously:</p>
<ol>
<li><p>Save business data.</p>
</li>
<li><p>Save an Outbox record in the same transaction.</p>
</li>
<li><p>Background worker publishes events.</p>
</li>
<li><p>Mark event as processed.</p>
</li>
</ol>
<p>Benefits:</p>
<ul>
<li><p>No lost messages</p>
</li>
<li><p>Eventual consistency</p>
</li>
<li><p>Reliable messaging</p>
</li>
</ul>
<p>Widely used with:</p>
<ul>
<li><p>Kafka</p>
</li>
<li><p>RabbitMQ</p>
</li>
<li><p>Event-driven architectures</p>
</li>
</ul>
<hr />
<h2>Final Thoughts</h2>
<p>Database design isn't about memorizing normalization rules or picking the newest database technology.</p>
<p>It's about choosing the right pattern for the right problem.</p>
<p>Senior engineers understand that:</p>
<ul>
<li><p>Every pattern has trade-offs.</p>
</li>
<li><p>Read and write workloads are rarely the same.</p>
</li>
<li><p>Scalability often starts with schema design.</p>
</li>
<li><p>Reliability is designed into the data model, not added later.</p>
</li>
<li><p>The best database architecture evolves with the business.</p>
</li>
</ul>
<p>The strongest systems are built on thoughtful data models—not just clever code.</p>
<p><strong>Which database design pattern has saved you the most pain in production?</strong></p>
]]></content:encoded></item><item><title><![CDATA[The 5-Step Prompt Framework I Use to Write Production-Ready Code]]></title><description><![CDATA["AI can generate code in seconds. Production-ready software still requires engineering."
When ChatGPT first appeared, many developers believed prompt engineering was about finding the perfect magic se]]></description><link>https://billliao.hashnode.dev/the-5-step-prompt-framework-i-use-to-write-production-ready-code</link><guid isPermaLink="true">https://billliao.hashnode.dev/the-5-step-prompt-framework-i-use-to-write-production-ready-code</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:01:01 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/f42ed01d-8f60-42b5-9d43-af0327b7893d.png" alt="" style="display:block;margin:0 auto" />

<p><em>"AI can generate code in seconds. Production-ready software still requires engineering."</em></p>
<p>When ChatGPT first appeared, many developers believed prompt engineering was about finding the perfect magic sentence.</p>
<p>After working with AI every day on real enterprise projects, I realized something different.</p>
<p>The quality of AI-generated code isn't determined by how clever your prompt is.</p>
<p>It's determined by how much context you provide.</p>
<p>Today, AI writes a lot of code.</p>
<p>But without architecture, constraints, testing requirements, and business context, that code is often incomplete, inconsistent, or impossible to merge into a production codebase.</p>
<p>That's why I rarely use one-line prompts anymore.</p>
<p>Instead, I use a structured five-step framework that consistently produces code I can actually review, refine, and ship.</p>
<hr />
<h2>Step 1: Define the Role</h2>
<p>Never start with:</p>
<blockquote>
<p>"Write a Spring Boot service."</p>
</blockquote>
<p>Instead, define who the AI should become.</p>
<p>For example:</p>
<pre><code class="language-plaintext">You are a Senior Java Software Architect with 15+ years of experience designing enterprise Spring Boot applications.
Follow Clean Architecture, SOLID principles, Java 21 best practices, and production-grade coding standards.
</code></pre>
<p>This immediately changes the style of the output.</p>
<p>Instead of generating tutorial code, the model begins reasoning like an experienced engineer.</p>
<hr />
<h2>Step 2: Provide Business Context</h2>
<p>AI doesn't know your project.</p>
<p>It doesn't know:</p>
<ul>
<li><p>your domain</p>
</li>
<li><p>your users</p>
</li>
<li><p>your workflows</p>
</li>
<li><p>your architecture</p>
</li>
<li><p>your naming conventions</p>
</li>
</ul>
<p>Instead of asking:</p>
<blockquote>
<p>Build a payment API.</p>
</blockquote>
<p>Try something like:</p>
<pre><code class="language-plaintext">This service belongs to an e-commerce platform.

Users can create orders.
Payments are processed asynchronously.
The application uses event-driven architecture with Kafka.
Payment status must be idempotent.
</code></pre>
<p>The more business context you provide, the fewer assumptions the model needs to make.</p>
<hr />
<h2>Step 3: Specify Technical Constraints</h2>
<p>This is where many prompts fail.</p>
<p>Production software is constrained by existing technology.</p>
<p>Include details such as:</p>
<ul>
<li><p>Java version</p>
</li>
<li><p>Spring Boot version</p>
</li>
<li><p>database</p>
</li>
<li><p>ORM</p>
</li>
<li><p>messaging platform</p>
</li>
<li><p>cloud provider</p>
</li>
<li><p>authentication</p>
</li>
<li><p>architecture pattern</p>
</li>
<li><p>coding standards</p>
</li>
<li><p>API style</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-plaintext">Use:

Java 21
Spring Boot 3.5
Spring Security
PostgreSQL
JPA
Liquibase
Kafka
JWT Authentication
Hexagonal Architecture
JUnit 5
Testcontainers
</code></pre>
<p>Now the generated code actually fits your ecosystem.</p>
<hr />
<h2>Step 4: Define Quality Expectations</h2>
<p>Instead of simply requesting functionality, define what "good" looks like.</p>
<p>Ask AI to include:</p>
<ul>
<li><p>validation</p>
</li>
<li><p>exception handling</p>
</li>
<li><p>logging</p>
</li>
<li><p>metrics</p>
</li>
<li><p>unit tests</p>
</li>
<li><p>integration tests</p>
</li>
<li><p>documentation</p>
</li>
<li><p>meaningful naming</p>
</li>
<li><p>transactional boundaries</p>
</li>
<li><p>performance considerations</p>
</li>
<li><p>security best practices</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-plaintext">The solution must be production-ready.

Include:

- validation
- error handling
- logging
- unit tests
- integration tests
- clean package structure
- meaningful comments only where necessary
</code></pre>
<p>The difference is dramatic.</p>
<p>You're no longer generating a code snippet.</p>
<p>You're generating an engineering solution.</p>
<hr />
<h2>Step 5: Ask AI to Review Its Own Work</h2>
<p>This is the step that many developers skip.</p>
<p>After generating the code, immediately ask:</p>
<pre><code class="language-plaintext">Review the implementation as a Senior Software Architect.

Identify:

- design flaws
- security issues
- performance risks
- concurrency problems
- scalability limitations
- missing edge cases

Then improve the implementation.
</code></pre>
<p>This second pass often finds issues that the first version missed.</p>
<p>Think of it as adding an automated design review before you even open your IDE.</p>
<hr />
<h2>Putting It All Together</h2>
<p>My prompts typically follow this structure:</p>
<pre><code class="language-plaintext">Role
↓
Business Context
↓
Technical Constraints
↓
Quality Requirements
↓
Self Review
</code></pre>
<p>This isn't about making prompts longer.</p>
<p>It's about making them more precise.</p>
<p>When AI understands the <em>why</em>, the <em>where</em>, the <em>constraints</em>, and the <em>definition of quality</em>, it produces code that's significantly closer to production standards.</p>
<hr />
<h2>The Biggest Lesson</h2>
<p>Over the past year, I've learned that AI is becoming less of a code generator and more of an engineering collaborator.</p>
<p>The engineers who get the best results aren't the ones who write the shortest prompts.</p>
<p>They're the ones who communicate requirements with the same clarity they'd use when mentoring a new team member.</p>
<p>Great prompts don't replace engineering.</p>
<p>They encode engineering.</p>
<p>And that's what turns AI from an autocomplete tool into a force multiplier for professional software development.</p>
<hr />
<p><strong>What does your AI coding workflow look like?</strong></p>
<p>Do you rely on short prompts, or do you provide architecture, constraints, and review instructions before asking AI to generate code?</p>
<p>I'd love to hear what's worked best for you.</p>
]]></content:encoded></item><item><title><![CDATA[The Difference Between Reliable and Resilient Systems]]></title><description><![CDATA[Many engineers use reliability and resilience interchangeably.
They're not the same.
And misunderstanding the difference often leads to architectures that perform perfectly in testing—but fail when re]]></description><link>https://billliao.hashnode.dev/the-difference-between-reliable-and-resilient-systems</link><guid isPermaLink="true">https://billliao.hashnode.dev/the-difference-between-reliable-and-resilient-systems</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 08:00:13 GMT</pubDate><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/6a6a760e81a689455254cda5/326ab6ac-8af4-4e91-baf1-8c82fbeeee49.png" alt="" style="display:block;margin:0 auto" />

<p>Many engineers use <strong>reliability</strong> and <strong>resilience</strong> interchangeably.</p>
<p>They're not the same.</p>
<p>And misunderstanding the difference often leads to architectures that perform perfectly in testing—but fail when real-world chaos arrives.</p>
<p>After designing distributed systems for decades, I've learned one simple truth:</p>
<blockquote>
<p><strong>Reliable systems aim to prevent failure. Resilient systems assume failure is inevitable.</strong></p>
</blockquote>
<p>That mindset changes everything.</p>
<hr />
<h3>Reliability: Keeping Things Working</h3>
<p>A <strong>reliable system</strong> consistently performs its intended function under expected operating conditions.</p>
<p>Its goal is straightforward:</p>
<ul>
<li><p>High availability</p>
</li>
<li><p>Correct functionality</p>
</li>
<li><p>Stable performance</p>
</li>
<li><p>Predictable behavior</p>
</li>
</ul>
<p>Reliability focuses on minimizing failures before they happen.</p>
<p>Typical techniques include:</p>
<ul>
<li><p>Redundant hardware</p>
</li>
<li><p>Unit and integration testing</p>
</li>
<li><p>Monitoring</p>
</li>
<li><p>Automated deployments</p>
</li>
<li><p>Database replication</p>
</li>
<li><p>High-quality code reviews</p>
</li>
<li><p>Capacity planning</p>
</li>
</ul>
<p>Imagine an online payment service.</p>
<p>If it processes 99.99% of payments successfully every day, most people would call it reliable.</p>
<p>And they'd be right.</p>
<p>But here's the question that matters:</p>
<p><strong>What happens when something unexpected occurs?</strong></p>
<hr />
<h3>Resilience: Thriving During Failure</h3>
<p>Resilience is different.</p>
<p>It measures how well a system <strong>responds, adapts, and recovers</strong> when things inevitably go wrong.</p>
<p>Failures are not exceptions.</p>
<p>They're expected.</p>
<p>Examples include:</p>
<ul>
<li><p>Network partitions</p>
</li>
<li><p>Cloud region outages</p>
</li>
<li><p>Database failures</p>
</li>
<li><p>Dependency timeouts</p>
</li>
<li><p>Traffic spikes</p>
</li>
<li><p>Human mistakes</p>
</li>
<li><p>Configuration errors</p>
</li>
</ul>
<p>A resilient system doesn't pretend these won't happen.</p>
<p>It prepares for them.</p>
<p>Instead of asking:</p>
<blockquote>
<p>"How do we prevent failure?"</p>
</blockquote>
<p>It asks:</p>
<blockquote>
<p>"How do we survive failure?"</p>
</blockquote>
<hr />
<h3>Reliable Systems Can Still Fail Catastrophically</h3>
<p>Consider two e-commerce platforms.</p>
<h3>Platform A</h3>
<ul>
<li><p>99.99% uptime</p>
</li>
<li><p>Fast response times</p>
</li>
<li><p>Excellent monitoring</p>
</li>
<li><p>Highly optimized database</p>
</li>
</ul>
<p>Everything works beautifully.</p>
<p>Until the payment provider becomes unavailable.</p>
<p>Suddenly:</p>
<ul>
<li><p>Orders fail</p>
</li>
<li><p>Checkout stops</p>
</li>
<li><p>Customers leave</p>
</li>
<li><p>Revenue disappears</p>
</li>
</ul>
<p>The platform was reliable.</p>
<p>But it wasn't resilient.</p>
<hr />
<h3>Platform B</h3>
<p>It experiences the same outage.</p>
<p>Instead of failing completely, it:</p>
<ul>
<li><p>Queues pending orders</p>
</li>
<li><p>Accepts carts for delayed payment</p>
</li>
<li><p>Switches to a backup payment provider</p>
</li>
<li><p>Notifies customers automatically</p>
</li>
<li><p>Recovers once the provider returns</p>
</li>
</ul>
<p>Customers experience minor delays.</p>
<p>Business continues.</p>
<p>That's resilience.</p>
<hr />
<h3>Reliability is About Components</h3>
<p>Reliability often improves individual components.</p>
<p>Examples include:</p>
<ul>
<li><p>Better databases</p>
</li>
<li><p>Faster APIs</p>
</li>
<li><p>Stronger testing</p>
</li>
<li><p>More stable infrastructure</p>
</li>
</ul>
<p>Every service becomes more dependable.</p>
<p>But distributed systems don't fail one component at a time.</p>
<p>They fail through interactions.</p>
<p>That's where resilience begins.</p>
<hr />
<h3>Resilience is About the Entire System</h3>
<p>Modern architectures contain dozens—or hundreds—of services.</p>
<p>Even if every service is 99.9% reliable:</p>
<ul>
<li><p>Dependencies fail</p>
</li>
<li><p>Networks become slow</p>
</li>
<li><p>Messages arrive late</p>
</li>
<li><p>APIs return partial responses</p>
</li>
<li><p>Caches become stale</p>
</li>
</ul>
<p>The architecture must continue delivering value despite these conditions.</p>
<p>Resilience is therefore a <strong>system-level property</strong>, not a component property.</p>
<hr />
<h3>Common Resilience Patterns</h3>
<p>Some of the most effective architectural patterns include:</p>
<h3>Circuit Breakers</h3>
<p>Prevent cascading failures by temporarily stopping calls to unhealthy services.</p>
<hr />
<h3>Retries with Backoff</h3>
<p>Handle transient failures without overwhelming downstream services.</p>
<hr />
<h3>Bulkheads</h3>
<p>Isolate failures so one overloaded service doesn't take down the entire platform.</p>
<hr />
<h3>Timeouts</h3>
<p>Never wait forever.</p>
<p>Slow services should fail fast.</p>
<hr />
<h3>Graceful Degradation</h3>
<p>If recommendations fail...</p>
<p>Still allow checkout.</p>
<p>If notifications fail...</p>
<p>Still process payments.</p>
<p>Not every feature is equally important.</p>
<hr />
<h3>Event-Driven Recovery</h3>
<p>Instead of blocking users synchronously:</p>
<ul>
<li><p>Publish events</p>
</li>
<li><p>Retry asynchronously</p>
</li>
<li><p>Recover automatically</p>
</li>
</ul>
<p>Users experience fewer failures while the system heals itself.</p>
<hr />
<h3>Chaos Engineering Changes the Conversation</h3>
<p>Traditional testing asks:</p>
<blockquote>
<p>Does the system work?</p>
</blockquote>
<p>Chaos engineering asks:</p>
<blockquote>
<p>Does the system still work when everything starts breaking?</p>
</blockquote>
<p>Organizations like Netflix popularized intentionally introducing failures into production because resilience cannot be proven by perfect conditions.</p>
<p>It must be demonstrated under imperfect ones.</p>
<hr />
<h3>AI Makes Resilience Even More Important</h3>
<p>AI-powered systems introduce new types of uncertainty.</p>
<p>LLMs may:</p>
<ul>
<li><p>Return inconsistent responses</p>
</li>
<li><p>Hit rate limits</p>
</li>
<li><p>Experience high latency</p>
</li>
<li><p>Produce invalid outputs</p>
</li>
<li><p>Become temporarily unavailable</p>
</li>
</ul>
<p>Building AI applications therefore requires architectural resilience.</p>
<p>Successful systems include:</p>
<ul>
<li><p>Fallback models</p>
</li>
<li><p>Prompt retries</p>
</li>
<li><p>Response validation</p>
</li>
<li><p>Confidence thresholds</p>
</li>
<li><p>Human approval workflows</p>
</li>
<li><p>Cached responses</p>
</li>
</ul>
<p>The question is no longer:</p>
<blockquote>
<p>"Is the model available?"</p>
</blockquote>
<p>It's:</p>
<blockquote>
<p>"How does the application continue delivering value when the model isn't?"</p>
</blockquote>
<hr />
<h3>Reliability + Resilience = Production Excellence</h3>
<p>The best architectures pursue both.</p>
<p>Reliability ensures systems work well.</p>
<p>Resilience ensures systems continue working when reality becomes messy.</p>
<p>You need both.</p>
<p>Because production isn't defined by your happy path.</p>
<p>It's defined by everything that happens after the happy path breaks.</p>
<hr />
<h3>Final Thoughts</h3>
<p>Great software engineers build reliable services.</p>
<p>Great architects build resilient systems.</p>
<p>The difference isn't just technical.</p>
<p>It's philosophical.</p>
<p>Reliable systems try to avoid failure.</p>
<p><strong>Resilient systems learn how to live with it.</strong></p>
<p>And in distributed systems, that difference often determines whether users notice a minor hiccup—or your company experiences a major outage.</p>
<hr />
<p><strong>What architectural pattern has improved resilience the most in your systems?</strong></p>
<p>I'd love to hear your experience in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[Step-by-Step: Implementing Hexagonal Architecture in Spring Boot]]></title><description><![CDATA["Good architecture isn't about making your code more complicated. It's about making future change less expensive."
As software systems grow, one problem appears over and over again.
Business logic bec]]></description><link>https://billliao.hashnode.dev/step-by-step-implementing-hexagonal-architecture-in-spring-boot</link><guid isPermaLink="true">https://billliao.hashnode.dev/step-by-step-implementing-hexagonal-architecture-in-spring-boot</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 07:59:12 GMT</pubDate><content:encoded><![CDATA[<p><em>"Good architecture isn't about making your code more complicated. It's about making future change less expensive."</em></p>
<p>As software systems grow, one problem appears over and over again.</p>
<p>Business logic becomes tightly coupled to frameworks, databases, REST APIs, messaging systems, and external services.</p>
<p>Eventually, every change becomes risky.</p>
<p>Need to replace MySQL with PostgreSQL? Unexpected refactoring.</p>
<p>Need to expose GraphQL instead of REST? Business logic changes.</p>
<p>Need to introduce Kafka? Half the application gets modified.</p>
<p>This isn't a Spring Boot problem.</p>
<p>It's an architecture problem.</p>
<p>One of the most effective ways to solve it is <strong>Hexagonal Architecture</strong> (also called <strong>Ports and Adapters</strong>).</p>
<p>After applying it in multiple enterprise systems, I've found that it dramatically improves maintainability, testability, and long-term flexibility.</p>
<p>Here's a practical step-by-step guide to implementing it in Spring Boot.</p>
<hr />
<h2>What Is Hexagonal Architecture?</h2>
<p>Hexagonal Architecture was introduced by Alistair Cockburn.</p>
<p>Its central idea is simple:</p>
<blockquote>
<p><strong>Business logic should not depend on external technologies. External technologies should depend on the business logic.</strong></p>
</blockquote>
<p>Instead of building applications around Spring, JPA, REST, or Kafka, we build them around the <strong>domain</strong>.</p>
<p>Everything else becomes replaceable.</p>
<p>Think of your application as a hexagon.</p>
<p>Outside the hexagon are adapters:</p>
<ul>
<li><p>REST APIs</p>
</li>
<li><p>Databases</p>
</li>
<li><p>Message brokers</p>
</li>
<li><p>External services</p>
</li>
<li><p>Command-line tools</p>
</li>
</ul>
<p>Inside the hexagon lives only business logic.</p>
<p>The business logic doesn't know or care who calls it.</p>
<hr />
<h2>Step 1: Define the Domain</h2>
<p>The domain contains:</p>
<ul>
<li><p>Entities</p>
</li>
<li><p>Value Objects</p>
</li>
<li><p>Business Rules</p>
</li>
<li><p>Domain Services</p>
</li>
</ul>
<p>Example:</p>
<pre><code class="language-plaintext">public class Order {

    private OrderId id;
    private CustomerId customerId;
    private List&lt;OrderItem&gt; items;

    public void confirm() {
        // business rules
    }
}
</code></pre>
<p>Notice what's missing.</p>
<p>No:</p>
<ul>
<li><p>@Entity</p>
</li>
<li><p>@Autowired</p>
</li>
<li><p>@Component</p>
</li>
<li><p>Spring annotations</p>
</li>
<li><p>JPA imports</p>
</li>
</ul>
<p>The domain is pure Java.</p>
<p>This makes it easy to test and independent of any framework.</p>
<hr />
<h2>Step 2: Define Input Ports (Use Cases)</h2>
<p>A port represents what the application can do.</p>
<p>Example:</p>
<pre><code class="language-plaintext">public interface CreateOrderUseCase {

    Order create(CreateOrderCommand command);

}
</code></pre>
<p>This is the API of your application.</p>
<p>Controllers call this interface.</p>
<p>Tests call this interface.</p>
<p>Even scheduled jobs call this interface.</p>
<p>Nobody depends on implementation details.</p>
<hr />
<h2>Step 3: Define Output Ports</h2>
<p>Business logic often needs external systems.</p>
<p>Instead of calling repositories directly, define interfaces.</p>
<p>Example:</p>
<pre><code class="language-plaintext">public interface OrderRepository {

    Order save(Order order);

    Optional&lt;Order&gt; findById(OrderId id);

}
</code></pre>
<p>Notice:</p>
<p>The domain defines the interface.</p>
<p>Infrastructure implements it.</p>
<p>Dependency direction remains inward.</p>
<hr />
<h2>Step 4: Implement the Use Case</h2>
<p>Application services coordinate business logic.</p>
<pre><code class="language-plaintext">@Service
public class CreateOrderService
        implements CreateOrderUseCase {

    private final OrderRepository repository;

    public Order create(CreateOrderCommand command) {

        Order order = Order.create(command);

        return repository.save(order);

    }
}
</code></pre>
<p>Notice something interesting.</p>
<p>This service doesn't know:</p>
<ul>
<li><p>JPA</p>
</li>
<li><p>SQL</p>
</li>
<li><p>PostgreSQL</p>
</li>
<li><p>MongoDB</p>
</li>
</ul>
<p>It only knows the repository interface.</p>
<hr />
<h2>Step 5: Build the REST Adapter</h2>
<p>Now expose the use case through HTTP.</p>
<pre><code class="language-plaintext">@RestController
@RequestMapping("/orders")
public class OrderController {

    private final CreateOrderUseCase useCase;

}
</code></pre>
<p>The controller translates:</p>
<p>HTTP → Domain</p>
<p>That's all.</p>
<p>No business rules should live here.</p>
<hr />
<h2>Step 6: Build the Database Adapter</h2>
<p>Infrastructure implements the output port.</p>
<pre><code class="language-plaintext">@Repository
public class JpaOrderRepository
        implements OrderRepository {

}
</code></pre>
<p>Internally, it may use:</p>
<ul>
<li><p>Spring Data JPA</p>
</li>
<li><p>Hibernate</p>
</li>
<li><p>JDBC</p>
</li>
<li><p>MyBatis</p>
</li>
</ul>
<p>The domain never notices.</p>
<p>Tomorrow you can replace JPA entirely.</p>
<hr />
<h2>Step 7: Add Messaging</h2>
<p>Suppose you publish events.</p>
<p>Instead of:</p>
<pre><code class="language-plaintext">kafkaTemplate.send(...)
</code></pre>
<p>inside business logic...</p>
<p>Define another output port.</p>
<pre><code class="language-plaintext">public interface EventPublisher {

    void publish(OrderCreatedEvent event);

}
</code></pre>
<p>Infrastructure implements it.</p>
<pre><code class="language-plaintext">KafkaEventPublisher
</code></pre>
<p>Later you can replace Kafka with RabbitMQ or AWS SNS without touching business logic.</p>
<hr />
<h2>Step 8: Organize the Project Structure</h2>
<p>A common Spring Boot layout looks like this:</p>
<pre><code class="language-plaintext">src
 ├── domain
 │     ├── model
 │     ├── ports
 │     └── services
 │
 ├── application
 │     ├── usecases
 │     └── services
 │
 ├── adapters
 │     ├── inbound
 │     │      ├── rest
 │     │      └── messaging
 │     │
 │     └── outbound
 │            ├── persistence
 │            ├── kafka
 │            └── clients
 │
 └── config
</code></pre>
<p>The separation becomes very clear.</p>
<hr />
<h2>Step 9: Testing Becomes Easy</h2>
<p>Testing business logic becomes trivial.</p>
<p>Instead of starting Spring Boot...</p>
<p>Instead of connecting databases...</p>
<p>Instead of mocking HTTP...</p>
<p>Simply mock the ports.</p>
<pre><code class="language-plaintext">OrderRepository repository = mock(...);

CreateOrderService service =
    new CreateOrderService(repository);
</code></pre>
<p>Tests become:</p>
<ul>
<li><p>Fast</p>
</li>
<li><p>Deterministic</p>
</li>
<li><p>Independent</p>
</li>
</ul>
<p>Many complete in milliseconds.</p>
<hr />
<h2>Step 10: Replace Infrastructure Without Fear</h2>
<p>Imagine these changes:</p>
<p>✅ REST → GraphQL</p>
<p>✅ MySQL → PostgreSQL</p>
<p>✅ Kafka → RabbitMQ</p>
<p>✅ Local Storage → AWS S3</p>
<p>✅ SMTP → SendGrid</p>
<p>In a layered architecture, these changes often ripple through multiple layers.</p>
<p>In Hexagonal Architecture, most changes stay confined to adapters.</p>
<p>The core business logic remains untouched.</p>
<p>That's the real payoff.</p>
<hr />
<h2>Common Mistakes</h2>
<p>I've reviewed many projects claiming to use Hexagonal Architecture but making these mistakes:</p>
<p>❌ JPA annotations inside domain entities</p>
<p>❌ Business logic inside controllers</p>
<p>❌ Spring dependencies everywhere</p>
<p>❌ Repository interfaces defined in infrastructure</p>
<p>❌ Domain objects returning HTTP responses</p>
<p>❌ Services calling Kafka directly</p>
<p>If your domain knows about Spring, it isn't truly framework-independent.</p>
<hr />
<h2>When Should You Use It?</h2>
<p>Hexagonal Architecture is an excellent fit for:</p>
<ul>
<li><p>Enterprise applications</p>
</li>
<li><p>Microservices</p>
</li>
<li><p>Financial systems</p>
</li>
<li><p>Healthcare platforms</p>
</li>
<li><p>Long-lived products</p>
</li>
<li><p>Complex business domains</p>
</li>
</ul>
<p>It may be unnecessary for:</p>
<ul>
<li><p>Small CRUD applications</p>
</li>
<li><p>Short-lived prototypes</p>
</li>
<li><p>Internal tools with limited complexity</p>
</li>
</ul>
<p>Architecture should match the problem—not every project needs maximum abstraction.</p>
<hr />
<h2>Final Thoughts</h2>
<p>Hexagonal Architecture isn't about adding layers for the sake of elegance.</p>
<p>It's about protecting what changes the least—your business rules—from what changes the most—frameworks, databases, APIs, and infrastructure.</p>
<p>Spring Boot will continue to evolve.</p>
<p>Databases will change.</p>
<p>Cloud providers will change.</p>
<p>Messaging platforms will change.</p>
<p>But your business logic should remain stable.</p>
<p>Design your application so that technology is a plugin, not the foundation.</p>
<p>That's the essence of Hexagonal Architecture.</p>
]]></content:encoded></item><item><title><![CDATA[Architects Don't Design Systems. They Design Decisions.]]></title><description><![CDATA[Most developers think software architects design systems.
They don't.
At least, that's not their most important job.
A great architect doesn't spend most of the day drawing boxes and arrows.
Instead, ]]></description><link>https://billliao.hashnode.dev/architects-don-t-design-systems-they-design-decisions</link><guid isPermaLink="true">https://billliao.hashnode.dev/architects-don-t-design-systems-they-design-decisions</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 07:58:30 GMT</pubDate><content:encoded><![CDATA[<p>Most developers think software architects design systems.</p>
<p>They don't.</p>
<p>At least, that's not their most important job.</p>
<p>A great architect doesn't spend most of the day drawing boxes and arrows.</p>
<p>Instead, they design the <strong>decisions</strong> that determine how those boxes will evolve over the next five years.</p>
<p>That's a fundamental difference.</p>
<hr />
<h3>Architecture Is the Sum of Decisions</h3>
<p>Take any mature system.</p>
<p>Its architecture isn't defined by the technology stack.</p>
<p>It's defined by thousands of decisions made over time.</p>
<ul>
<li><p>Should this service own the customer profile?</p>
</li>
<li><p>Should communication be synchronous or event-driven?</p>
</li>
<li><p>Where should business rules live?</p>
</li>
<li><p>Should consistency be immediate or eventual?</p>
</li>
<li><p>Should this capability become a shared platform or remain domain-specific?</p>
</li>
<li><p>What should never be coupled together?</p>
</li>
</ul>
<p>Every one of these decisions shapes the future.</p>
<p>The diagram is just a snapshot.</p>
<p>The decisions create the architecture.</p>
<hr />
<h3>The Biggest Mistake Architects Make</h3>
<p>Many architects believe their responsibility ends after producing the "perfect" architecture diagram.</p>
<p>Reality is different.</p>
<p>Six months later...</p>
<p>New requirements appear.</p>
<p>Teams change.</p>
<p>Priorities shift.</p>
<p>Budgets shrink.</p>
<p>AI tools accelerate development.</p>
<p>Suddenly that beautiful architecture document becomes obsolete.</p>
<p>Not because it was wrong.</p>
<p>Because it couldn't evolve.</p>
<p>Good architecture isn't about predicting the future.</p>
<p>It's about making future decisions easier.</p>
<hr />
<h3>Architecture Is Decision Economics</h3>
<p>Every architectural decision has a cost.</p>
<p>Some costs are obvious.</p>
<p>Others don't appear until years later.</p>
<p>For example:</p>
<p>Choosing microservices isn't choosing scalability.</p>
<p>You're choosing:</p>
<ul>
<li><p>Distributed transactions</p>
</li>
<li><p>Network latency</p>
</li>
<li><p>Operational complexity</p>
</li>
<li><p>Monitoring</p>
</li>
<li><p>Deployment pipelines</p>
</li>
<li><p>Team coordination</p>
</li>
<li><p>Higher cognitive load</p>
</li>
</ul>
<p>Choosing a monolith isn't choosing simplicity.</p>
<p>You're choosing:</p>
<ul>
<li><p>Shared deployments</p>
</li>
<li><p>Larger codebases</p>
</li>
<li><p>Different scaling characteristics</p>
</li>
<li><p>Different ownership models</p>
</li>
</ul>
<p>Neither is universally correct.</p>
<p>Architects don't optimize for today's requirements.</p>
<p>They optimize for tomorrow's trade-offs.</p>
<hr />
<h3>Great Architects Design Decision Frameworks</h3>
<p>Instead of answering every question themselves, great architects create frameworks that help everyone make better decisions.</p>
<p>For example:</p>
<p>Instead of saying:</p>
<blockquote>
<p>"Use Kafka."</p>
</blockquote>
<p>They define:</p>
<blockquote>
<p>"Use asynchronous messaging whenever business workflows can tolerate eventual consistency."</p>
</blockquote>
<p>Instead of saying:</p>
<blockquote>
<p>"Build a microservice."</p>
</blockquote>
<p>They define:</p>
<blockquote>
<p>"Create a new service only when business ownership, deployment independence, and scaling requirements justify the operational cost."</p>
</blockquote>
<p>Notice the difference?</p>
<p>The architect isn't making one decision.</p>
<p>They're designing how future decisions should be made.</p>
<hr />
<h3>The AI Era Makes This Even More Important</h3>
<p>AI can now generate code.</p>
<p>It can build APIs.</p>
<p>It can write tests.</p>
<p>It can even produce infrastructure templates.</p>
<p>But AI still struggles with organizational decisions.</p>
<p>Questions like:</p>
<ul>
<li><p>Where should boundaries exist?</p>
</li>
<li><p>Which trade-off matters most?</p>
</li>
<li><p>Should performance outweigh maintainability?</p>
</li>
<li><p>Is this abstraction worth its complexity?</p>
</li>
<li><p>What technical debt is acceptable?</p>
</li>
</ul>
<p>These aren't coding problems.</p>
<p>They're decision problems.</p>
<p>Ironically, as AI gets better at implementation, architecture becomes <em>more</em> valuable—not less.</p>
<p>Because implementation is becoming cheaper.</p>
<p>Poor decisions are becoming more expensive.</p>
<hr />
<h3>The Best Architecture Is Invisible</h3>
<p>People often praise elegant architectures.</p>
<p>But the best architectures are almost invisible.</p>
<p>You notice them because teams move faster.</p>
<p>Developers don't argue about ownership.</p>
<p>Changes remain localized.</p>
<p>Deployments become routine.</p>
<p>Incidents are easier to understand.</p>
<p>New engineers become productive quickly.</p>
<p>That's the result of thousands of good decisions—not one impressive design.</p>
<hr />
<h3>What Architects Should Really Deliver</h3>
<p>Instead of delivering architecture documents, architects should deliver:</p>
<ul>
<li><p>Clear engineering principles</p>
</li>
<li><p>Decision-making guidelines</p>
</li>
<li><p>Well-defined system boundaries</p>
</li>
<li><p>Explicit trade-offs</p>
</li>
<li><p>Consistent architectural patterns</p>
</li>
<li><p>Shared technical vocabulary</p>
</li>
<li><p>Governance that enables rather than blocks</p>
</li>
</ul>
<p>These become the operating system for engineering teams.</p>
<hr />
<h3>Final Thoughts</h3>
<p>Software architecture is often mistaken for designing systems.</p>
<p>In reality, systems are temporary.</p>
<p>Decisions are permanent.</p>
<p>Every technology eventually changes.</p>
<p>Frameworks evolve.</p>
<p>Programming languages come and go.</p>
<p>Cloud platforms improve.</p>
<p>AI transforms development.</p>
<p>But the quality of architectural decisions continues to shape software long after today's technology becomes obsolete.</p>
<p>The best architects aren't remembered for the diagrams they created.</p>
<p>They're remembered for the decisions they enabled.</p>
<p>And that's what great architecture has always been about.</p>
]]></content:encoded></item><item><title><![CDATA[How AI Is Changing System Design: From Whiteboard Interviews to Real-World Architecture]]></title><description><![CDATA[For years, system design interviews have followed a familiar script.
Design a URL shortener.
Design Twitter.
Design Uber.
Design a distributed cache.
Candidates draw boxes, databases, load balancers, ]]></description><link>https://billliao.hashnode.dev/how-ai-is-changing-system-design-from-whiteboard-interviews-to-real-world-architecture</link><guid isPermaLink="true">https://billliao.hashnode.dev/how-ai-is-changing-system-design-from-whiteboard-interviews-to-real-world-architecture</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 07:57:47 GMT</pubDate><content:encoded><![CDATA[<p>For years, system design interviews have followed a familiar script.</p>
<p>Design a URL shortener.</p>
<p>Design Twitter.</p>
<p>Design Uber.</p>
<p>Design a distributed cache.</p>
<p>Candidates draw boxes, databases, load balancers, queues, and APIs on a virtual whiteboard. Interviewers evaluate scalability, availability, consistency, and trade-offs.</p>
<p>These exercises have been valuable for teaching distributed systems.</p>
<p>But AI is quietly changing what "good system design" actually means.</p>
<p>The next generation of architects won't just design systems for humans.</p>
<p>They'll design systems where humans and AI collaborate.</p>
<hr />
<h3>The Old Goal: Build Software That Humans Can Understand</h3>
<p>Traditional architecture focuses on people.</p>
<p>Can another engineer understand the code?</p>
<p>Can a new developer learn the API?</p>
<p>Can operations troubleshoot failures?</p>
<p>Can the team maintain the system five years from now?</p>
<p>These principles remain essential.</p>
<p>However, a new participant has entered every architecture discussion:</p>
<p><strong>AI.</strong></p>
<p>AI reads documentation.</p>
<p>AI writes code.</p>
<p>AI reviews pull requests.</p>
<p>AI generates tests.</p>
<p>AI explains legacy systems.</p>
<p>AI debugs production issues.</p>
<p>Increasingly, AI becomes an active engineering teammate rather than just a coding assistant.</p>
<p>That changes the architecture itself.</p>
<hr />
<h3>AI Doesn't Read Software Like Humans</h3>
<p>Humans rely on experience.</p>
<p>They infer intent from context.</p>
<p>They recognize business patterns.</p>
<p>They ask questions.</p>
<p>Large language models work differently.</p>
<p>They infer meaning from context windows.</p>
<p>They rely on naming consistency.</p>
<p>They struggle with hidden assumptions.</p>
<p>They perform better when architecture is explicit instead of implicit.</p>
<p>The systems that feel intuitive to experienced developers aren't always easy for AI to reason about.</p>
<p>This creates an interesting challenge.</p>
<p><strong>A maintainable architecture for humans isn't automatically an AI-friendly architecture.</strong></p>
<hr />
<h3>System Design Is Becoming Context Design</h3>
<p>Traditional system design answers questions like:</p>
<ul>
<li><p>How do services communicate?</p>
</li>
<li><p>Where is state stored?</p>
</li>
<li><p>How do we scale horizontally?</p>
</li>
<li><p>How do we ensure resilience?</p>
</li>
</ul>
<p>Modern AI systems introduce another set of questions:</p>
<ul>
<li><p>How does an AI understand the domain?</p>
</li>
<li><p>Where does it retrieve knowledge?</p>
</li>
<li><p>How is business context preserved?</p>
</li>
<li><p>How does it choose the right tools?</p>
</li>
<li><p>How do we verify its reasoning?</p>
</li>
<li><p>How do we prevent hallucinations?</p>
</li>
<li><p>How do we observe decision quality?</p>
</li>
</ul>
<p>These are architecture problems.</p>
<p>Not prompt engineering problems.</p>
<hr />
<h3>New Components Are Appearing in Every Architecture Diagram</h3>
<p>Five years ago, architecture diagrams looked familiar.</p>
<p>API Gateway.</p>
<p>Microservices.</p>
<p>Databases.</p>
<p>Message Queues.</p>
<p>Caches.</p>
<p>Today many production architectures also include:</p>
<ul>
<li><p>Vector databases</p>
</li>
<li><p>Retrieval pipelines (RAG)</p>
</li>
<li><p>AI Gateways</p>
</li>
<li><p>Model routers</p>
</li>
<li><p>Prompt management</p>
</li>
<li><p>Agent orchestration</p>
</li>
<li><p>Context stores</p>
</li>
<li><p>Evaluation pipelines</p>
</li>
<li><p>Guardrails</p>
</li>
<li><p>Observability for AI</p>
</li>
<li><p>Human approval workflows</p>
</li>
<li><p>Memory services</p>
</li>
</ul>
<p>These aren't experimental anymore.</p>
<p>They're becoming first-class architectural building blocks.</p>
<hr />
<h3>The Whiteboard Question Is Changing</h3>
<p>Imagine two interview questions.</p>
<h3>Traditional</h3>
<p>Design a payment platform that processes one million transactions per minute.</p>
<h3>AI Era</h3>
<p>Design an AI-powered payment investigation platform where autonomous agents:</p>
<ul>
<li><p>investigate failed payments</p>
</li>
<li><p>retrieve historical transactions</p>
</li>
<li><p>explain compliance decisions</p>
</li>
<li><p>escalate uncertain cases</p>
</li>
<li><p>collaborate with human analysts</p>
</li>
<li><p>continuously learn from feedback</p>
</li>
</ul>
<p>Notice what changed.</p>
<p>The challenge isn't just throughput.</p>
<p>It's reasoning.</p>
<p>Context.</p>
<p>Trust.</p>
<p>Observability.</p>
<p>Governance.</p>
<p>These dimensions barely appeared in classic system design interviews.</p>
<p>Soon they'll become standard.</p>
<hr />
<h3>The New Architecture Skills</h3>
<p>Future architects need more than distributed systems knowledge.</p>
<p>They also need to understand:</p>
<h3>Context Engineering</h3>
<p>How information flows through AI systems.</p>
<h3>Tool Orchestration</h3>
<p>How AI safely interacts with internal services.</p>
<h3>AI Observability</h3>
<p>Understanding why an agent made a decision.</p>
<h3>Evaluation Frameworks</h3>
<p>Measuring correctness instead of just uptime.</p>
<h3>Human-in-the-Loop Design</h3>
<p>Knowing when AI should stop and ask for help.</p>
<h3>Multi-Agent Collaboration</h3>
<p>Designing systems where specialized agents cooperate without creating chaos.</p>
<p>These are becoming architectural competencies rather than AI research topics.</p>
<hr />
<h3>AI Doesn't Replace System Design</h3>
<p>Ironically, AI makes system design even more important.</p>
<p>Generating code is easier than ever.</p>
<p>Designing resilient systems is still difficult.</p>
<p>If anything, the architectural burden increases because we're now designing for two audiences:</p>
<p>Humans.</p>
<p>And intelligent machines.</p>
<p>The best systems will make both productive.</p>
<hr />
<h3>Final Thoughts</h3>
<p>For decades, system design was about scaling software.</p>
<p>Now it's about scaling decision-making.</p>
<p>The architect's responsibility is expanding from designing distributed systems to designing distributed intelligence.</p>
<p>Whiteboard interviews may still ask you to design Twitter.</p>
<p>Real-world architecture may ask you to design an ecosystem where engineers, AI agents, and production systems collaborate safely, transparently, and efficiently.</p>
<p>That is a much harder problem.</p>
<p>And a far more interesting one.</p>
<hr />
<p><strong>What do you think?</strong></p>
<p>If system design interviews were created from scratch today, what new questions should every senior engineer or software architect be expected to answer?</p>
<p>I'd love to hear your perspective.</p>
]]></content:encoded></item><item><title><![CDATA[5 Rate Limiting Strategies Every Backend Developer Should Know]]></title><description><![CDATA[API traffic is growing faster than ever.
Whether you're building microservices, AI-powered applications, or public APIs, rate limiting is no longer optional—it's one of the most important mechanisms f]]></description><link>https://billliao.hashnode.dev/5-rate-limiting-strategies-every-backend-developer-should-know</link><guid isPermaLink="true">https://billliao.hashnode.dev/5-rate-limiting-strategies-every-backend-developer-should-know</guid><dc:creator><![CDATA[Bill Liao]]></dc:creator><pubDate>Sat, 08 Aug 2026 07:56:11 GMT</pubDate><content:encoded><![CDATA[<p>API traffic is growing faster than ever.</p>
<p>Whether you're building microservices, AI-powered applications, or public APIs, <strong>rate limiting is no longer optional</strong>—it's one of the most important mechanisms for protecting system stability.</p>
<p>I've seen production systems fail not because the infrastructure was too small, but because there was <strong>no intelligent traffic control</strong>.</p>
<p>The good news?</p>
<p>Choosing the right rate limiting algorithm can dramatically improve reliability without adding massive complexity.</p>
<p>Here are five strategies every backend developer should understand.</p>
<hr />
<h2>Why Rate Limiting Matters</h2>
<p>Without rate limiting, a backend service can quickly suffer from:</p>
<ul>
<li><p>Traffic spikes</p>
</li>
<li><p>DDoS attacks</p>
</li>
<li><p>Infinite retry storms</p>
</li>
<li><p>Misbehaving clients</p>
</li>
<li><p>AI agents generating excessive requests</p>
</li>
<li><p>Expensive third-party API overuse</p>
</li>
</ul>
<p>Rate limiting helps ensure:</p>
<p>✅ Fair resource allocation</p>
<p>✅ Predictable latency</p>
<p>✅ Better user experience</p>
<p>✅ Lower infrastructure costs</p>
<hr />
<h2>1. Fixed Window Counter</h2>
<p><strong>How it works</strong></p>
<p>Requests are counted within a fixed time window.</p>
<p>Example:</p>
<pre><code class="language-plaintext">100 requests per minute
</code></pre>
<p>Every new minute resets the counter.</p>
<pre><code class="language-plaintext">Minute 1

██████████ 100 requests

↓

Counter resets

Minute 2

██████████ 100 requests
</code></pre>
<h3>Advantages</h3>
<ul>
<li><p>Extremely simple</p>
</li>
<li><p>Fast</p>
</li>
<li><p>Low memory usage</p>
</li>
<li><p>Easy to implement in Redis</p>
</li>
</ul>
<h3>Drawback</h3>
<p>A client can send:</p>
<pre><code class="language-plaintext">100 requests at 12:00:59

+

100 requests at 12:01:00
</code></pre>
<p>Result:</p>
<pre><code class="language-plaintext">200 requests in 2 seconds
</code></pre>
<p>This burst may overwhelm downstream services.</p>
<p><strong>Best for</strong></p>
<ul>
<li><p>Internal services</p>
</li>
<li><p>Low-traffic APIs</p>
</li>
<li><p>Simple business applications</p>
</li>
</ul>
<hr />
<h2>2. Sliding Window Log</h2>
<p>Instead of resetting every minute, every request timestamp is stored.</p>
<p>For each new request:</p>
<pre><code class="language-plaintext">Remove expired timestamps

Count remaining

Accept or reject
</code></pre>
<p>Example</p>
<pre><code class="language-plaintext">10:00:01

10:00:05

10:00:15

10:00:40

Current time

↓

Keep only last 60 seconds
</code></pre>
<h3>Advantages</h3>
<ul>
<li><p>Highly accurate</p>
</li>
<li><p>Smooth traffic control</p>
</li>
<li><p>No boundary burst problem</p>
</li>
</ul>
<h3>Drawback</h3>
<ul>
<li><p>High memory usage</p>
</li>
<li><p>More expensive calculations</p>
</li>
<li><p>Doesn't scale well for extremely high QPS</p>
</li>
</ul>
<p><strong>Best for</strong></p>
<ul>
<li><p>Authentication APIs</p>
</li>
<li><p>Financial systems</p>
</li>
<li><p>Critical business endpoints</p>
</li>
</ul>
<hr />
<h2>3. Sliding Window Counter</h2>
<p>This is an optimization over the sliding log.</p>
<p>Instead of storing every request, it combines two adjacent windows and calculates a weighted count.</p>
<p>Example</p>
<pre><code class="language-plaintext">Current Window

████████

Previous Window

██████

↓

Weighted Total
</code></pre>
<h3>Advantages</h3>
<ul>
<li><p>Much lower memory usage</p>
</li>
<li><p>Near-sliding-window accuracy</p>
</li>
<li><p>Efficient for distributed systems</p>
</li>
</ul>
<h3>Drawback</h3>
<p>Slightly less accurate than storing every timestamp.</p>
<p><strong>Best for</strong></p>
<ul>
<li><p>Most production APIs</p>
</li>
<li><p>SaaS platforms</p>
</li>
<li><p>Cloud services</p>
</li>
</ul>
<p>This is one of the most common production choices.</p>
<hr />
<h2>4. Token Bucket</h2>
<p>One of the most popular algorithms used by cloud providers.</p>
<p>Imagine a bucket filled with tokens.</p>
<pre><code class="language-plaintext">Bucket Capacity

100 Tokens
</code></pre>
<p>Requests consume tokens.</p>
<pre><code class="language-plaintext">Incoming Request

↓

Take One Token

↓

Allowed
</code></pre>
<p>Tokens refill continuously.</p>
<pre><code class="language-plaintext">+5 tokens/sec
</code></pre>
<p>If the bucket is empty:</p>
<pre><code class="language-plaintext">Request Rejected
</code></pre>
<h3>Advantages</h3>
<ul>
<li><p>Allows short bursts</p>
</li>
<li><p>Smooth recovery</p>
</li>
<li><p>Excellent user experience</p>
</li>
<li><p>Widely used in API gateways</p>
</li>
</ul>
<p><strong>Best for</strong></p>
<ul>
<li><p>Public APIs</p>
</li>
<li><p>Mobile applications</p>
</li>
<li><p>AI inference services</p>
</li>
<li><p>Cloud-native systems</p>
</li>
</ul>
<hr />
<h2>5. Leaky Bucket</h2>
<p>Instead of limiting requests directly, requests enter a queue.</p>
<pre><code class="language-plaintext">Incoming Traffic

↓↓↓↓↓↓↓↓↓↓

Bucket

↓

↓

↓

Constant Output Rate
</code></pre>
<p>Water leaks out at a fixed speed.</p>
<p>If the bucket becomes full:</p>
<pre><code class="language-plaintext">New Requests

↓

Dropped
</code></pre>
<h3>Advantages</h3>
<ul>
<li><p>Produces very stable traffic</p>
</li>
<li><p>Prevents sudden spikes</p>
</li>
<li><p>Protects downstream systems</p>
</li>
</ul>
<h3>Drawback</h3>
<p>Requests may wait in the queue, increasing latency.</p>
<p><strong>Best for</strong></p>
<ul>
<li><p>Message processing</p>
</li>
<li><p>Event pipelines</p>
</li>
<li><p>Streaming platforms</p>
</li>
<li><p>Payment systems</p>
</li>
</ul>
<hr />
<h2>Which Strategy Should You Choose?</h2>
<p>StrategyBurst HandlingAccuracyMemoryComplexityBest Use CaseFixed WindowPoorLowVery LowVery LowInternal APIsSliding LogExcellentHighestHighMediumFinancial APIsSliding CounterVery GoodHighLowMediumMost REST APIsToken BucketExcellentHighLowMediumPublic APIs &amp; AI ServicesLeaky BucketExcellentMediumMediumMediumQueues &amp; Streaming</p>
<hr />
<h2>Distributed Systems Considerations</h2>
<p>Modern applications rarely run on a single server.</p>
<p>In distributed environments, rate limiting often relies on shared state using technologies such as:</p>
<ul>
<li><p>Redis</p>
</li>
<li><p>API Gateway</p>
</li>
<li><p>NGINX</p>
</li>
<li><p>Envoy</p>
</li>
<li><p>Kong</p>
</li>
<li><p>Spring Cloud Gateway</p>
</li>
<li><p>Kubernetes Ingress Controllers</p>
</li>
</ul>
<p>A common production architecture looks like this:</p>
<pre><code class="language-plaintext">Client

↓

Load Balancer

↓

API Gateway

↓

Redis Counter

↓

Microservices
</code></pre>
<p>This ensures rate limits remain consistent across multiple application instances.</p>
<hr />
<h2>AI Changes the Game</h2>
<p>AI agents don't behave like human users.</p>
<p>An autonomous workflow may trigger:</p>
<ul>
<li><p>hundreds of tool calls</p>
</li>
<li><p>recursive retries</p>
</li>
<li><p>parallel reasoning</p>
</li>
<li><p>multiple LLM requests</p>
</li>
<li><p>vector database queries</p>
</li>
<li><p>external API calls</p>
</li>
</ul>
<p>Traditional per-user rate limiting is often insufficient.</p>
<p>Modern AI platforms increasingly combine multiple dimensions, including:</p>
<ul>
<li><p>User limits</p>
</li>
<li><p>API key limits</p>
</li>
<li><p>Tenant quotas</p>
</li>
<li><p>Model-specific limits</p>
</li>
<li><p>Token budgets</p>
</li>
<li><p>Cost-aware throttling</p>
</li>
<li><p>Adaptive rate limiting based on system load</p>
</li>
</ul>
<p>As AI adoption grows, intelligent traffic management becomes a core architectural capability rather than just an API protection feature.</p>
<hr />
<h2>Final Thoughts</h2>
<p>There is no universal "best" rate limiting algorithm.</p>
<p>The right choice depends on your system's goals:</p>
<ul>
<li><p>Need maximum simplicity? → Fixed Window.</p>
</li>
<li><p>Need perfect accuracy? → Sliding Window Log.</p>
</li>
<li><p>Need a balanced production solution? → Sliding Window Counter.</p>
</li>
<li><p>Need to support bursts gracefully? → Token Bucket.</p>
</li>
<li><p>Need smooth, predictable throughput? → Leaky Bucket.</p>
</li>
</ul>
<p>The best backend systems don't just process requests quickly—they know <strong>when to slow them down</strong>.</p>
<p><strong>What rate limiting strategy are you using in production, and what lessons have you learned? I'd love to hear your experience.</strong></p>
]]></content:encoded></item></channel></rss>