<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Blog on David Parry</title>
    <link>https://davidparry.com/blog/</link>
    <description>Recent content in Blog on David Parry</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 29 Jul 2026 20:49:00 -0500</lastBuildDate>
    <atom:link href="https://davidparry.com/blog/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>🔐 Where Security Starts</title>
      <link>https://davidparry.com/blog/2026/07/29/where-security-starts/</link>
      <pubDate>Wed, 29 Jul 2026 20:49:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/07/29/where-security-starts/</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/where-security-starts-linkedin.png&#34; alt=&#34;Where Security Starts — a layered software system standing on a shielded foundation, with one unprotected side cracking apart&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;Today I was asked a simple question:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&amp;ldquo;Where does security start when it comes to software development?&amp;rdquo;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;My answer is still the same: security starts with the developer.&lt;/p&gt;&#xA;&lt;p&gt;Not with the annual checkbox. Not with the quarterly training video. Not with the login screen that proves someone completed a security class. Those things may be required in the enterprise, and they can be useful, but they are not where real software security starts.&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/where-security-starts-linkedin.png&#34; alt=&#34;Where Security Starts — a layered software system standing on a shielded foundation, with one unprotected side cracking apart&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;Today I was asked a simple question:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&amp;ldquo;Where does security start when it comes to software development?&amp;rdquo;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;My answer is still the same: security starts with the developer.&lt;/p&gt;&#xA;&lt;p&gt;Not with the annual checkbox. Not with the quarterly training video. Not with the login screen that proves someone completed a security class. Those things may be required in the enterprise, and they can be useful, but they are not where real software security starts.&lt;/p&gt;&#xA;&lt;p&gt;Real security starts when a developer understands what secure code means.&lt;/p&gt;&#xA;&lt;p&gt;Secure code is not just code that compiles. It is code that handles input correctly, protects data, uses authentication and authorization properly, manages errors without leaking sensitive information, avoids unsafe memory behavior, uses approved libraries, and follows architecture patterns the enterprise already trusts.&lt;/p&gt;&#xA;&lt;p&gt;Secure software development is not one activity. It is a system.&lt;/p&gt;&#xA;&lt;p&gt;It includes secure requirements, threat modeling, framework selection, code review, automated testing, dependency scanning, software composition analysis, CI/CD controls, and production observability. &lt;a href=&#34;https://csrc.nist.gov/projects/ssdf&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;NIST&amp;rsquo;s Secure Software Development Framework&lt;/a&gt;&#xA; supports this view by recommending secure development practices across the software development life cycle, not after the software is already built.&lt;/p&gt;&#xA;&lt;h2 id=&#34;security-starts-before-the-pull-request&#34;&gt;Security Starts Before the Pull Request&lt;/h2&gt;&#xA;&lt;p&gt;A lot of enterprises require developers to take security training every few months. That is fine, but training alone is not enough.&lt;/p&gt;&#xA;&lt;p&gt;The better question is this:&lt;/p&gt;&#xA;&lt;p&gt;Can the developer recognize insecure code before it becomes production code?&lt;/p&gt;&#xA;&lt;p&gt;That is where code review matters. Reviewing code is not just about formatting, naming, or whether the code follows the team&amp;rsquo;s style guide. Good review asks deeper questions:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Is this input trusted?&lt;/li&gt;&#xA;&lt;li&gt;Is this framework being used correctly?&lt;/li&gt;&#xA;&lt;li&gt;Is this custom implementation replacing something the platform already solves?&lt;/li&gt;&#xA;&lt;li&gt;Does this change weaken authorization?&lt;/li&gt;&#xA;&lt;li&gt;Does this expose data?&lt;/li&gt;&#xA;&lt;li&gt;Does this introduce a dependency that should not be here?&lt;/li&gt;&#xA;&lt;li&gt;Does this generated code contain a vulnerability that looks harmless at first glance?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Security starts when developers are trained to think this way and when the engineering process reinforces that behavior every day.&lt;/p&gt;&#xA;&lt;h2 id=&#34;frameworks-are-security-controls&#34;&gt;Frameworks Are Security Controls&lt;/h2&gt;&#xA;&lt;p&gt;One of the biggest mistakes teams make is treating frameworks as convenience libraries instead of security controls.&lt;/p&gt;&#xA;&lt;p&gt;A mature framework is not only there to save keystrokes. It gives the team hardened defaults, proven patterns, predictable configuration, tested integrations, and community-reviewed behavior. In enterprise software, using a proven framework correctly is often more secure than building custom code to solve a problem the ecosystem already solved.&lt;/p&gt;&#xA;&lt;p&gt;This is especially true in Java and Spring-based enterprise systems. Authentication, authorization, input handling, validation, serialization, database access, observability, and configuration should not be invented from scratch by every team.&lt;/p&gt;&#xA;&lt;p&gt;Custom code is sometimes necessary. Custom security infrastructure should be treated with extreme caution.&lt;/p&gt;&#xA;&lt;p&gt;A recent &lt;a href=&#34;https://blogs.vmware.com/tanzu/vmware-tanzu-spring-delivers-slsa-l3-compliant-java-dependencies-for-spring-boot-2-7-x-3-x-and-4-x/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;VMware Tanzu Spring announcement&lt;/a&gt;&#xA; is a good example of this principle in practice. Tanzu Spring announced SLSA Level 3 compliant Java dependencies for Spring Boot 2.7.x, 3.x, and 4.x. More specifically, this is not a blanket claim that every Spring Boot application is automatically SLSA Level 3 compliant. The announcement is about clean room builds and provenance for the Spring Boot Java dependency tree available through the Spring Enterprise Repository.&lt;/p&gt;&#xA;&lt;p&gt;That distinction matters.&lt;/p&gt;&#xA;&lt;p&gt;The value is not just that developers are using Spring Boot. The value is that the enterprise can consume a verified dependency supply chain from a trusted source. According to the announcement, Tanzu Spring customers now have access to secure, clean room builds of the Java dependency tree for Spring Boot 2.7.x, 3.x, and 4.x, including provenance for more than 5,000 verified Java library dependencies from a single trusted source.&lt;/p&gt;&#xA;&lt;p&gt;That is a security control.&lt;/p&gt;&#xA;&lt;p&gt;Framework security is no longer only about the code the framework gives you. It is also about the supply chain behind the framework. Modern enterprise applications depend on large transitive dependency trees. If those dependencies come from unknown, unaudited, or unverifiable build paths, the application inherits that risk.&lt;/p&gt;&#xA;&lt;p&gt;Mature enterprise frameworks and trusted artifact sources reduce the amount of custom security-critical code teams write, and they improve confidence in the dependencies teams consume.&lt;/p&gt;&#xA;&lt;p&gt;That does not remove the need for code review, dependency scanning, SBOMs, SAST, SCA, or runtime controls. But it does prove the larger point: security starts earlier than the scan. It starts with the engineering choices developers and architects make before the code ever reaches production.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-buffer-overflow-lesson-still-matters&#34;&gt;The Buffer Overflow Lesson Still Matters&lt;/h2&gt;&#xA;&lt;p&gt;The classic example is still the buffer overflow.&lt;/p&gt;&#xA;&lt;p&gt;A buffer overflow is not just an old C or C++ problem people mention in security classes. It is a reminder that vulnerabilities often start as ordinary engineering mistakes. A boundary was not checked. Input was trusted. A library was used incorrectly. A reviewer missed it. A generated code change was accepted without enough scrutiny.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://cwe.mitre.org/top25/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;MITRE&amp;rsquo;s CWE Top 25&lt;/a&gt;&#xA; continues to include serious software weakness classes such as cross-site scripting, SQL injection, missing authorization, out-of-bounds write, path traversal, use-after-free, out-of-bounds read, command injection, code injection, classic buffer overflow, deserialization of untrusted data, improper input validation, and improper access control.&lt;/p&gt;&#xA;&lt;p&gt;These are not abstract academic concerns. They are common and dangerous weakness classes that can lead to system compromise, data exposure, privilege escalation, or service disruption.&lt;/p&gt;&#xA;&lt;p&gt;This is why security cannot belong only to the security team.&lt;/p&gt;&#xA;&lt;p&gt;Security has to be part of how code is written, reviewed, tested, packaged, and shipped.&lt;/p&gt;&#xA;&lt;h2 id=&#34;where-snyk-like-tools-fit&#34;&gt;Where Snyk-Like Tools Fit&lt;/h2&gt;&#xA;&lt;p&gt;Tools such as Snyk, Dependabot, OpenSSF Scorecard, SBOM tooling, SAST, SCA, container scanners, and infrastructure-as-code scanners play an important role.&lt;/p&gt;&#xA;&lt;p&gt;But they do not all solve the same problem.&lt;/p&gt;&#xA;&lt;p&gt;Software composition analysis helps identify vulnerable dependencies. It tells you whether the packages, frameworks, containers, and infrastructure components you are using have known vulnerabilities.&lt;/p&gt;&#xA;&lt;p&gt;That matters because modern software is assembled as much as it is written. Most enterprise applications depend on open-source packages, transitive dependencies, containers, build tools, plugins, and infrastructure configuration. Risk can enter through code your team wrote, but it can also enter through code your team imported.&lt;/p&gt;&#xA;&lt;p&gt;That is different from code review.&lt;/p&gt;&#xA;&lt;p&gt;Dependency scanning asks:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Are we using something vulnerable?&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Code review asks:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Did we build something vulnerable?&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;You need both.&lt;/p&gt;&#xA;&lt;p&gt;An SBOM can tell you what is inside your software. SCA can tell you whether known components have known CVEs. Code review, static analysis, tests, and architectural review help determine whether your own code introduces risk.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-ai-should-change&#34;&gt;What AI Should Change&lt;/h2&gt;&#xA;&lt;p&gt;This is where AI-assisted code review becomes important.&lt;/p&gt;&#xA;&lt;p&gt;If AI is used only to generate more code faster, we are going to create security debt faster.&lt;/p&gt;&#xA;&lt;p&gt;But if AI is used to review code, challenge assumptions, identify risky patterns, check framework usage, explain potential vulnerabilities, and enforce secure engineering standards, then it can improve the software delivery process.&lt;/p&gt;&#xA;&lt;p&gt;That does not mean AI will eliminate vulnerabilities.&lt;/p&gt;&#xA;&lt;p&gt;It should mean fewer obvious vulnerabilities make it through review.&lt;/p&gt;&#xA;&lt;p&gt;The future should not be a world where tools keep finding the same preventable issues after the fact. The goal should be to catch more issues before they become releases, incidents, CVEs, emergency patches, or customer-facing failures.&lt;/p&gt;&#xA;&lt;p&gt;AI-assisted review should help reduce repeated classes of defects:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Poor input validation&lt;/li&gt;&#xA;&lt;li&gt;Unsafe deserialization&lt;/li&gt;&#xA;&lt;li&gt;Broken authorization&lt;/li&gt;&#xA;&lt;li&gt;Improper error handling&lt;/li&gt;&#xA;&lt;li&gt;Insecure dependency usage&lt;/li&gt;&#xA;&lt;li&gt;Custom security code replacing mature framework features&lt;/li&gt;&#xA;&lt;li&gt;Weak tests around security-sensitive behavior&lt;/li&gt;&#xA;&lt;li&gt;Risky generated code accepted without review&lt;/li&gt;&#xA;&lt;li&gt;Configuration mistakes&lt;/li&gt;&#xA;&lt;li&gt;Architecture decisions that expand the attack surface&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This only works if AI is part of a governed engineering system. AI review must be tied to rules, standards, architecture, trusted frameworks, CI gates, and human accountability.&lt;/p&gt;&#xA;&lt;p&gt;The goal is not to replace security tools. The goal is to move more security knowledge into the daily development workflow.&lt;/p&gt;&#xA;&lt;h2 id=&#34;better-software-should-mean-fewer-preventable-vulnerabilities&#34;&gt;Better Software Should Mean Fewer Preventable Vulnerabilities&lt;/h2&gt;&#xA;&lt;p&gt;Here is the part I think enterprises should start expecting.&lt;/p&gt;&#xA;&lt;p&gt;If teams are using better code review, stronger framework defaults, secure dependency sources, SCA, SBOMs, SAST, and AI-assisted review, then the number of preventable vulnerabilities should go down.&lt;/p&gt;&#xA;&lt;p&gt;Not all vulnerabilities will disappear. That would be an unrealistic claim.&lt;/p&gt;&#xA;&lt;p&gt;But many classes of avoidable issues should become less common:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The obvious injection flaw.&lt;/li&gt;&#xA;&lt;li&gt;The missing authorization check.&lt;/li&gt;&#xA;&lt;li&gt;The unsafe generated code.&lt;/li&gt;&#xA;&lt;li&gt;The custom security implementation that should have used the framework.&lt;/li&gt;&#xA;&lt;li&gt;The vulnerable dependency pulled from an untrusted source.&lt;/li&gt;&#xA;&lt;li&gt;The transitive dependency nobody knew existed.&lt;/li&gt;&#xA;&lt;li&gt;The risky configuration that should have been caught before merge.&lt;/li&gt;&#xA;&lt;li&gt;The insecure pattern that keeps showing up across services.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This is where AI-assisted review can add real value. It can help reviewers focus. It can explain why a pattern is risky. It can compare code against secure coding rules. It can help developers learn while they work. It can make security feedback faster, more consistent, and closer to the code.&lt;/p&gt;&#xA;&lt;p&gt;But the enterprise still needs the full system: secure frameworks, trusted artifact repositories, dependency scanning, SBOMs, automated tests, CI/CD gates, human review, observability, and accountability.&lt;/p&gt;&#xA;&lt;p&gt;AI should make the system better.&lt;/p&gt;&#xA;&lt;p&gt;It should not become another excuse to skip the system.&lt;/p&gt;&#xA;&lt;h2 id=&#34;security-is-an-engineering-discipline&#34;&gt;Security Is an Engineering Discipline&lt;/h2&gt;&#xA;&lt;p&gt;Security does not start when the security team scans the finished product. It starts earlier, when engineering makes the right decision before the code exists.&lt;/p&gt;&#xA;&lt;p&gt;It starts with developers who know how to write secure code and reviewers who know what insecure code looks like. It starts with teams that choose proven frameworks instead of inventing security-critical plumbing, architects who reduce risk through design, and enterprises that consume dependencies from trusted, auditable sources. And it starts when dependency scanning, SBOMs, SAST, SCA, AI-assisted review, and human review work together instead of in silos.&lt;/p&gt;&#xA;&lt;p&gt;The enterprise does not need more checkbox security.&lt;/p&gt;&#xA;&lt;p&gt;It needs secure engineering discipline.&lt;/p&gt;&#xA;&lt;p&gt;And that starts with the developer.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>🔍 MCP: Enterprise Trust Through Traceability</title>
      <link>https://davidparry.com/blog/2026/07/22/mcp-enterprise-trust-through-traceability/</link>
      <pubDate>Wed, 22 Jul 2026 09:30:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/07/22/mcp-enterprise-trust-through-traceability/</guid>
      <description>&lt;p&gt;The headline everyone pulled out of the &lt;a href=&#34;https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Model Context Protocol (MCP) 2026-07-28 Release Candidate&lt;/a&gt;&#xA; is that MCP is going &lt;strong&gt;stateless&lt;/strong&gt;. Fair enough — it&amp;rsquo;s a genuine architectural improvement. Stateless protocols scale horizontally without sticky sessions, they cut operational complexity, and they make the infrastructure easier to deploy and reason about.&lt;/p&gt;&#xA;&lt;p&gt;But I don&amp;rsquo;t think statelessness is the change that will matter most for enterprise adoption. I think &lt;strong&gt;traceability&lt;/strong&gt; is.&lt;/p&gt;&#xA;&lt;p&gt;When I gave a conference talk on building MCP servers earlier this year, the hallway questions afterward were rarely about what a server could do. They were about what it takes to run one: how the traffic shows up in gateway logs, what a trace looks like when a tool call fails, whether any of it can be audited later. So when the release candidate landed, I read it with those questions in mind.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;The headline everyone pulled out of the &lt;a href=&#34;https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Model Context Protocol (MCP) 2026-07-28 Release Candidate&lt;/a&gt;&#xA; is that MCP is going &lt;strong&gt;stateless&lt;/strong&gt;. Fair enough — it&amp;rsquo;s a genuine architectural improvement. Stateless protocols scale horizontally without sticky sessions, they cut operational complexity, and they make the infrastructure easier to deploy and reason about.&lt;/p&gt;&#xA;&lt;p&gt;But I don&amp;rsquo;t think statelessness is the change that will matter most for enterprise adoption. I think &lt;strong&gt;traceability&lt;/strong&gt; is.&lt;/p&gt;&#xA;&lt;p&gt;When I gave a conference talk on building MCP servers earlier this year, the hallway questions afterward were rarely about what a server could do. They were about what it takes to run one: how the traffic shows up in gateway logs, what a trace looks like when a tool call fails, whether any of it can be audited later. So when the release candidate landed, I read it with those questions in mind.&lt;/p&gt;&#xA;&lt;p&gt;Two changes stood out to me, and they work as a pair.&lt;/p&gt;&#xA;&lt;p&gt;The first is &lt;a href=&#34;https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;SEP-2243&lt;/a&gt;&#xA;, which makes two headers, &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt;, required on the Streamable HTTP transport. The spec is clear about their job: they exist so load balancers, gateways, and rate limiters can route on the operation without inspecting the JSON-RPC body. What interests me is the side effect. Every layer of the stack now gets a piece of standardized, protocol-level metadata — and standardized metadata is exactly the raw material enterprise observability and audit tooling is built on.&lt;/p&gt;&#xA;&lt;p&gt;The second is &lt;a href=&#34;https://modelcontextprotocol.io/seps/414-request-meta&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;SEP-414&lt;/a&gt;&#xA;, which locks the &lt;strong&gt;W3C Trace Context&lt;/strong&gt; keys — &lt;code&gt;traceparent&lt;/code&gt;, &lt;code&gt;tracestate&lt;/code&gt;, and &lt;code&gt;baggage&lt;/code&gt; — into the spec as reserved keys inside &lt;code&gt;params._meta&lt;/code&gt; of every JSON-RPC request. Note where they live: in the message, not in HTTP headers. That&amp;rsquo;s deliberate. MCP is transport-agnostic and stdio has no headers, and a single Streamable HTTP connection can multiplex many JSON-RPC messages, so trace context has to belong to the individual request rather than the connection. A trace that starts in the host application can now follow a tool call through the client SDK, the MCP server, and whatever the server calls downstream, and land in an OpenTelemetry-compatible backend as one span tree:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;jsonrpc&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2.0&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;method&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tools/call&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;params&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;arguments&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;New York&amp;#34;&lt;/span&gt; },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;_meta&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;traceparent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Several SDKs were already doing this — the release candidate fixes the key names so traces correlate across implementations instead of by convention. And in the same release, MCP&amp;rsquo;s own logging capability is deprecated, with OpenTelemetry named as the path for structured observability. Read those together and the direction is unambiguous: the protocol didn&amp;rsquo;t invent its own tracing story, it adopted the one enterprises already run.&lt;/p&gt;&#xA;&lt;p&gt;That matters because enterprises are not building greenfield. They already have API gateways, OpenTelemetry, centralized logging, SIEM platforms, operational dashboards, and distributed tracing — and years of trust invested in all of it. A new protocol gets adopted when it slots into that existing machinery, not when it asks to be operated as a special case beside it. Routing headers at the transport layer, W3C Trace Context in the message — small design decisions with a large consequence, because together they mean MCP can be operated alongside everything else instead of babysat beside it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-enterprise-traceability-looks-like&#34;&gt;What Enterprise Traceability Looks Like&lt;/h2&gt;&#xA;&lt;p&gt;The important property is that the new metadata enriches an existing trace without changing how distributed tracing works.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                    End User&#xA;                       │&#xA;                       ▼&#xA;                  API Gateway ────────────────┐&#xA;                       │                      │&#xA;                       ▼                      │&#xA;            Spring Boot / Spring AI           │&#xA;                       │                      │&#xA;                       ▼                      ▼&#xA;                  MCP Client         Logs · Metrics · SIEM&#xA;                       │                      ▲&#xA;    Mcp-Method         │                      │&#xA;    Mcp-Name    ──────►│ ─────────────────────┤&#xA;    (HTTP headers)     │                      │&#xA;                       │                      │&#xA;    traceparent        │                      │&#xA;    tracestate  ──────►│                      │&#xA;    (in _meta)         │                      │&#xA;                       ▼                      │&#xA;                  MCP Server ─────────────────┤&#xA;                       │                      │&#xA;                       ▼                      │&#xA;                      Tool ───────────────────┘&#xA;&#xA;  W3C Trace Context propagates through every hop above — over HTTP&#xA;  headers on ordinary hops, inside _meta on the MCP hop. The routing&#xA;  headers add protocol-specific metadata to that same trace.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The distributed trace flows exactly as it does today. The routing headers give the infrastructure something to key on, and the &lt;code&gt;_meta&lt;/code&gt; trace keys keep the MCP hop stitched into the trace you&amp;rsquo;re already collecting.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-questions-a-platform-team-asks-first&#34;&gt;The Questions a Platform Team Asks First&lt;/h2&gt;&#xA;&lt;p&gt;Enterprise engineering organizations rarely get stuck because they can&amp;rsquo;t scale a service. They get stuck because they can&amp;rsquo;t answer questions after the fact:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Which MCP method executed, on which server, running which tool?&lt;/li&gt;&#xA;&lt;li&gt;Which distributed trace contains this interaction, and how does it line up with the gateway logs?&lt;/li&gt;&#xA;&lt;li&gt;Which agent initiated the request, and why did it fail?&lt;/li&gt;&#xA;&lt;li&gt;Can we reproduce it — and can we audit it six months from now?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These aren&amp;rsquo;t exotic questions. They&amp;rsquo;re the ones a platform team asks before it agrees to support new infrastructure in production, and standardized protocol metadata is what makes them answerable. With &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt; riding alongside the trace context, requests correlate across services and gateways without custom parsing, dashboards and rate limits key off the operation instead of the payload, and the telemetry looks the same across different MCP implementations. That&amp;rsquo;s the difference between something a platform team can confidently run in production and something that stays a proof of concept.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fan-out-is-where-this-gets-expensive&#34;&gt;Fan-Out Is Where This Gets Expensive&lt;/h2&gt;&#xA;&lt;p&gt;The stakes go up as organizations move toward agentic software. A single user request can fan out across multiple agents, several MCP servers, and dozens of tool invocations before anything comes back. Without standardized metadata, reconstructing what actually happened in that fan-out is slow and often guesswork.&lt;/p&gt;&#xA;&lt;p&gt;With the routing headers and trace-context keys in place, each MCP interaction correlates cleanly with the telemetry you already collect, and the whole system gets easier to operate and troubleshoot. This is the same argument I keep coming back to: agentic systems only become &lt;em&gt;enterprise&lt;/em&gt; systems when they&amp;rsquo;re as observable, governable, and auditable as every other critical application in the estate. This release moves MCP another step toward that bar.&lt;/p&gt;&#xA;&lt;h2 id=&#34;more-than-stateless&#34;&gt;More Than Stateless&lt;/h2&gt;&#xA;&lt;p&gt;Statelessness earns its headlines. Simpler deployments, better scalability, and lower operational complexity are real wins, and the &lt;a href=&#34;https://modelcontextprotocol.io/specification/draft/changelog&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;changelog&lt;/a&gt;&#xA; is worth reading in full. But the larger enterprise story is the standardized metadata that integrates naturally with the observability and tracing ecosystems organizations already trust.&lt;/p&gt;&#xA;&lt;p&gt;Platform teams want a protocol that fits the infrastructure they already run, and the technologies that win in the enterprise are rarely the ones with the longest feature lists.&lt;/p&gt;&#xA;&lt;p&gt;The claim I&amp;rsquo;ll defend is narrower than &amp;ldquo;MCP is enterprise-ready&amp;rdquo;: two required routing headers and three reserved trace-context keys quietly gave every gateway, trace, and audit log a common vocabulary for MCP traffic. In my experience, that&amp;rsquo;s the kind of unglamorous change that decides whether a protocol makes it out of the proof-of-concept stage.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>💰 Skills vs MCP: What the Token Bill Actually Measures</title>
      <link>https://davidparry.com/blog/2026/07/20/skills-vs-mcp-what-the-token-bill-actually-measures/</link>
      <pubDate>Mon, 20 Jul 2026 09:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/07/20/skills-vs-mcp-what-the-token-bill-actually-measures/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Move deterministic business rules out of the model and into code.&lt;/strong&gt; For the one workload I benchmarked, that single decision cut hosted-model cost by 32% on Claude Opus 4.8 and 76% on GPT-5, ran two to five times faster, and eliminated a silent age-calculation error because an authoritative clock replaced the model&amp;rsquo;s guess. That is the conclusion. Everything below is how I measured it, why the popular “Skills versus MCP” framing is the wrong dividing line, and where the result stops being defensible.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;strong&gt;Move deterministic business rules out of the model and into code.&lt;/strong&gt; For the one workload I benchmarked, that single decision cut hosted-model cost by 32% on Claude Opus 4.8 and 76% on GPT-5, ran two to five times faster, and eliminated a silent age-calculation error because an authoritative clock replaced the model&amp;rsquo;s guess. That is the conclusion. Everything below is how I measured it, why the popular “Skills versus MCP” framing is the wrong dividing line, and where the result stops being defensible.&lt;/p&gt;&#xA;&lt;p&gt;The question that sent me down this path came after a conference talk I gave about building MCP servers. Another engineer asked me something blunt: &lt;em&gt;Why are you still talking about MCP when Skills can do all of this?&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;My first reaction was equally blunt. If I worked for a model provider, I might prefer the design that keeps more work inside the model. I work for the organization paying the bill, so I want deterministic business rules to run in ordinary code whenever that is practical.&lt;/p&gt;&#xA;&lt;p&gt;That is my opinion, not evidence of a vendor conspiracy. Providers have good reasons to make models more capable, and customers are free to choose the architecture. Still, the incentive is worth noticing: when a model does more work, customers usually buy more inference. The two hosted models in this benchmark also price output tokens at five to eight times their input-token rate.&lt;/p&gt;&#xA;&lt;p&gt;So I built a small benchmark to see how much that choice mattered. The result supported my architectural instinct, but it also exposed a problem with the original framing of this article. “Skills versus MCP” is not the real dividing line. The real one is &lt;strong&gt;model-executed rules versus code-executed rules&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-i-actually-compared&#34;&gt;What I actually compared&lt;/h2&gt;&#xA;&lt;p&gt;A Skill is a package, not an execution environment. The &lt;a href=&#34;https://agentskills.io/home&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;open Agent Skills specification&lt;/a&gt;&#xA; allows a Skill to contain instructions, reference material, assets, &lt;strong&gt;and executable scripts&lt;/strong&gt;. &lt;a href=&#34;https://help.openai.com/en/articles/20001066&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;OpenAI’s description of Skills&lt;/a&gt;&#xA; likewise says a Skill can include code.&lt;/p&gt;&#xA;&lt;p&gt;MCP is a protocol through which a model-facing client can discover and call tools. An MCP server can contain business logic, but it can just as easily wrap a database, an API, a clock, or a bad nondeterministic service. The protocol itself does not make the result correct.&lt;/p&gt;&#xA;&lt;p&gt;The benchmark therefore compared these specific implementations:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Instruction-only Skill:&lt;/strong&gt; the Skill contains the rules and catalog in Markdown. The model performs the calculations.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;MCP-backed tool:&lt;/strong&gt; a Rust server performs the calculations and returns structured data. The model calls it and formats the response.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Prompt-only baseline:&lt;/strong&gt; the same rules are placed directly in the system prompt and the model performs the calculations.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That is a narrower and more useful comparison: &lt;strong&gt;model-executed rules versus code-executed rules&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;An executable Skill would make a legitimate fourth configuration, but “the Skill contains code” is not enough information. There are two very different ways an agent can use that code. It can run the script in a real software runtime, or it can read the source and attempt to follow the logic itself. The second option is still model-executed business logic. In fact, it may be worse than giving the model concise rules: the source consumes more context, while the model remains free to miss a branch, mishandle a boundary, or improvise around the implementation.&lt;/p&gt;&#xA;&lt;p&gt;Running the script is different. Once ordinary software receives the same validated inputs, it can produce the same authoritative output as the Rust server. The remaining uncertainty sits in the orchestration around it. Will the model load the Skill, notice the script, invoke it instead of doing the calculation itself, construct the command and arguments correctly, and present the returned value without “correcting” it? If script execution is merely suggested in prose, the architecture still depends on the model choosing the deterministic path on every request.&lt;/p&gt;&#xA;&lt;p&gt;If the agent runtime exposes the bundled script as a required, typed capability and reliably routes the request through it, I would expect the token and latency profile to resemble the MCP-backed path. At that point, however, the important win comes from executable code being treated as authoritative, not from the Skill label. If the model is expected to read and mentally execute the bundled source, I would expect more tokens without gaining the guarantee that made the code worth writing.&lt;/p&gt;&#xA;&lt;p&gt;I have not tested either version of that fourth configuration yet. A useful follow-up would measure them separately: &lt;strong&gt;Skill plus enforced script execution&lt;/strong&gt; and &lt;strong&gt;Skill plus source code for the model to interpret&lt;/strong&gt;. Combining those into one “executable Skill” result would hide the architectural difference, but both versions still hit the core economic question: how much material must the model read, reason about, and generate before the code runs? Loading Skill instructions or source, deciding to invoke a script, constructing the command and arguments, and then consuming the result all use tokens. Executable code makes the calculation authoritative; it does not make the orchestration free. My expectation is that asking the model to interpret source will be the most expensive version, while enforced script execution should be closer to MCP. Whether it is cheaper or more expensive than a compact, typed MCP tool call is something the fourth benchmark must measure rather than assume.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-workload&#34;&gt;The workload&lt;/h2&gt;&#xA;&lt;p&gt;The customer record contains a birth date, ZIP code, ordered interests, and a maximum budget. The system must calculate age, classify the customer, filter a small activity catalog, apply discounts, rank eligible activities, and return the best matches.&lt;/p&gt;&#xA;&lt;p&gt;The harness ran all three configurations 15 times on each of three models:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Claude Opus 4.8&lt;/li&gt;&#xA;&lt;li&gt;GPT-5&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;qwen3:30b&lt;/code&gt;, served locally through Ollama&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That produced 135 responses. The harness recorded provider-reported input, output, and total tokens; model and tool calls; end-to-end latency; estimated API cost; and the final text. The Rust server, Skill, prompts, harness, tests, and raw JSON are in the &lt;a href=&#34;https://github.com/davidparry/skill-vs-mcp&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;public benchmark repository&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;There is one important wrinkle. The customer request did &lt;strong&gt;not&lt;/strong&gt; include the current date. The MCP server read the host’s clock, while the instruction-only configurations had no equivalent clock tool. An earlier draft of this article said the date was part of the scenario. It was not.&lt;/p&gt;&#xA;&lt;p&gt;That means the cost and latency comparison uses the same user request, but the later age comparison is not a controlled test of code versus model arithmetic. It also tests access to current state. I discuss those results separately rather than pretending the difference does not exist.&lt;/p&gt;&#xA;&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;&#xA;&lt;p&gt;Every number below is the mean of 15 successful runs. Prices are standard, non-cached API prices at the time of the test. GPT-5 was &lt;a href=&#34;https://developers.openai.com/api/docs/models/gpt-5&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;$1.25 per million input tokens and $10 per million output tokens&lt;/a&gt;&#xA;. Claude Opus 4.8 was &lt;a href=&#34;https://www.anthropic.com/claude/opus&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;$5 and $25&lt;/a&gt;&#xA;, respectively.&lt;/p&gt;&#xA;&lt;p&gt;The “text variation” column is the mean pairwise character-sequence difference among the 15 final answers, calculated with Python’s &lt;code&gt;SequenceMatcher&lt;/code&gt;. It measures how different the rendered responses were. It does &lt;strong&gt;not&lt;/strong&gt; measure semantic correctness, business-rule determinism, or auditability.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Claude Opus 4.8&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Model calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Latency (s)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Text variation&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,210&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,193&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,403&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00605&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.02983&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.03588&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;12.5&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;48.5%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;MCP-backed tool&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,071&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;561&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,632&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.01036&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.01403&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.02438&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;9.0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;38.8%&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,131&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,231&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,362&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00566&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.03078&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.03643&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;12.9&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;61.9%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;GPT-5&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Model calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Latency (s)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Text variation&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;802&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,089&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,891&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00100&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.02089&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.02189&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;16.1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;76.8%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;MCP-backed tool&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;963&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;408&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,371&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00120&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00408&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00529&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;4.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;38.7%&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;783&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,320&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;3,103&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00098&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.02320&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.02418&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;20.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;67.8%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;qwen3:30b (local via Ollama on an Apple M4 Max with 128 GB RAM)&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total tokens&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Model calls&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Input $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Output $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Total $&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Latency (s)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Text variation&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;898&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;4,192&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;5,090&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;43.4&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;76.7%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;MCP-backed tool&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,480&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1,086&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2,565&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;10.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;58.9%&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;882&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;5,112&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;5,994&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$0.00&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;$0.00&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;54.4&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;73.2%&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;Cost per 1,000 requests&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Model&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;MCP-backed tool&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Instruction-only Skill&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Prompt only&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Tool vs Skill&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;Tool vs Prompt&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Claude Opus 4.8&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$24.38&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$35.88&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$36.43&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;32% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;33% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;GPT-5&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$5.29&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$21.89&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;$24.18&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;76% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;78% less&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;what-those-numbers-support&#34;&gt;What those numbers support&lt;/h2&gt;&#xA;&lt;p&gt;The tool-backed path sent more input because the request included a tool schema and required a second model call. It still cost less because it used far fewer completion tokens. On these models, for this task, the price difference between input and output was large enough to overwhelm the extra round trip.&lt;/p&gt;&#xA;&lt;p&gt;The same implementation was also faster in this sweep. It reduced mean latency from 12.5 to 9.0 seconds against the instruction-only Skill on Opus, from 16.1 to 4.8 seconds on GPT-5, and from 43.4 to 10.8 seconds on the local model. The local comparison matters because there is no per-token invoice to blame. Generating fewer tokens still consumed less wall-clock time on my hardware.&lt;/p&gt;&#xA;&lt;p&gt;The provider APIs reported completion-token usage; those counts are what I priced. Depending on the provider and model, that usage can include billed reasoning tokens that never appear in the visible answer. It would be inaccurate to say every calculation was literally printed to the user.&lt;/p&gt;&#xA;&lt;p&gt;The text-variation result is interesting but weaker. Tool-backed answers were more alike in all three sets, which makes sense because the model received the same structured result each time. A character-level similarity score is sensitive to headings, wording, and answer length, though. I would not use it as evidence that the business logic is deterministic. For that, I would test the function outputs directly.&lt;/p&gt;&#xA;&lt;p&gt;Most important, this is one synthetic workload with one customer and one catalog. It shows that moving this set of rules into code reduced tokens, latency, and hosted-model cost. It does not prove that an MCP call is always cheaper. A large tool schema, a chatty tool response, network latency, retries, or a tiny calculation could reverse the result.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-clock-result-useful-but-not-a-fair-arithmetic-contest&#34;&gt;The clock result: useful, but not a fair arithmetic contest&lt;/h2&gt;&#xA;&lt;p&gt;All configurations returned the same three activities in the same order:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Guided Nature Walk — $20&lt;/li&gt;&#xA;&lt;li&gt;Mountain Hiking Tour — $45&lt;/li&gt;&#xA;&lt;li&gt;Jazz Club Evening — $60&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The reported age differed:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Config&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Claude Opus 4.8&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;GPT-5&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;qwen3:30b&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;MCP-backed tool&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Instruction-only Skill&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;35 (14/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;33 (12/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Prompt only&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;35 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;36 (15/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;strong&gt;33 (14/15)&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The benchmark ran on July 19, 2026, and the customer was born on May 15, 1990, so 36 was correct. The Rust service read that date from the system clock. Opus often behaved as if the year were 2025, and qwen often behaved as if it were 2023. GPT-5 returned the exact benchmark date even though the user request and harness system prompt did not supply it. The data does not tell me whether that date came from model behavior or provider-side context, so calling it a “guess” would go beyond the evidence.&lt;/p&gt;&#xA;&lt;p&gt;This result demonstrates a real production lesson: if an answer depends on current state, give the system an authoritative source for that state. It does &lt;strong&gt;not&lt;/strong&gt; demonstrate that MCP is uniquely able to provide one. A bundled Skill script, a local command, a conventional API, or an MCP server could all read a clock.&lt;/p&gt;&#xA;&lt;p&gt;It also explains why the bad ages did not change the recommendations. Ages 33, 35, and 36 fall into the same age band, generation, and discount tier in this catalog. Near a boundary, the error could matter. A 65-year-old calculated as 62 would miss the senior discount; an 18-year-old calculated as 17 could cross discount and eligibility rules. Those are examples of what the defect &lt;em&gt;could&lt;/em&gt; cause, not outcomes observed in this run.&lt;/p&gt;&#xA;&lt;p&gt;For repeatable testing, the server already has a &lt;code&gt;FixedClock&lt;/code&gt; implementation. The benchmark should use it, or pass the same explicit date to all three configurations. I plan to add that controlled case before making broader correctness claims.&lt;/p&gt;&#xA;&lt;h2 id=&#34;where-i-draw-the-boundary&#34;&gt;Where I draw the boundary&lt;/h2&gt;&#xA;&lt;p&gt;This experiment reminded me of reading a SQL execution plan. Returning the right rows is necessary, but it is not the end of the engineering work. At scale, I also care about the cost of getting those rows, the behavior under failure, and whether I can test the logic without asking a probabilistic model to repeat it.&lt;/p&gt;&#xA;&lt;p&gt;My rule of thumb is now:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Use Skill instructions for judgment, workflow, and reusable operating procedures.&lt;/li&gt;&#xA;&lt;li&gt;Use executable code for calculations, eligibility, prices, policy rules, and access to authoritative state.&lt;/li&gt;&#xA;&lt;li&gt;Use MCP when that executable capability should be a shared, discoverable tool with a stable interface. A bundled Skill script or ordinary service may be simpler when it should not.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That is also what I look for when interviewing engineers who use AI. This is a personal hiring preference, not a universal standard. I care less about whether a candidate can make a model produce working code and more about whether they can explain which parts belong in model reasoning, which parts need a deterministic boundary, and what the choice costs at production volume.&lt;/p&gt;&#xA;&lt;p&gt;The benchmark changed my wording, not my conclusion. “MCP beats Skills” is too broad. The claim I can defend is this: &lt;strong&gt;for this workload, code-executed business rules beat model-executed rules on cost and latency, and an authoritative clock prevented a silent data error.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;That is enough to influence an architecture. It is not enough to declare a universal winner.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;em&gt;Reproduce the experiment or challenge it in the &lt;a href=&#34;https://github.com/davidparry/skill-vs-mcp&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;skill-vs-mcp repository&lt;/a&gt;&#xA;. The raw 15-run result set used here is included.&lt;/em&gt;&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>☕ Java at Thirty: Still One of the Strongest Bets in the Enterprise</title>
      <link>https://davidparry.com/blog/2026/07/18/java-at-thirty-still-one-of-the-strongest-bets-in-the-enterprise/</link>
      <pubDate>Sat, 18 Jul 2026 11:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/07/18/java-at-thirty-still-one-of-the-strongest-bets-in-the-enterprise/</guid>
      <description>&lt;p&gt;Three decades after its release, Java is still solving the problems that matter most to enterprise software. That staying power is easy to dismiss as inertia. I don&amp;rsquo;t think it is. I think it&amp;rsquo;s the predictable result of a platform that optimized for the things large organizations actually care about — and kept investing in them long after the hype cycle moved on.&lt;/p&gt;&#xA;&lt;p&gt;Let me be careful about the claim I&amp;rsquo;m making, though.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;Three decades after its release, Java is still solving the problems that matter most to enterprise software. That staying power is easy to dismiss as inertia. I don&amp;rsquo;t think it is. I think it&amp;rsquo;s the predictable result of a platform that optimized for the things large organizations actually care about — and kept investing in them long after the hype cycle moved on.&lt;/p&gt;&#xA;&lt;p&gt;Let me be careful about the claim I&amp;rsquo;m making, though.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-honest-version-of-the-claim&#34;&gt;The honest version of the claim&lt;/h3&gt;&#xA;&lt;p&gt;I&amp;rsquo;m not going to tell you Java is &lt;em&gt;the optimal language&lt;/em&gt;. That&amp;rsquo;s an opinion dressed up as a fact, and it falls apart the moment you change the context — Java is not my first reach for a data-science notebook, a systems kernel, or a quick shell script.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the claim I&amp;rsquo;ll actually defend: &lt;strong&gt;Java remains one of the strongest choices for enterprise software where long-term maintainability, stability, and operational reliability are the dominant concerns.&lt;/strong&gt; That&amp;rsquo;s a narrower statement, and it&amp;rsquo;s a stronger one, because it names the conditions under which it&amp;rsquo;s true.&lt;/p&gt;&#xA;&lt;p&gt;For most organizations, success is rarely about using the newest language. It&amp;rsquo;s about delivering reliable systems, controlling operational risk, hiring engineers at scale, and maintaining software for a decade or more. Judge Java against &lt;em&gt;those&lt;/em&gt; goals and its longevity stops looking like nostalgia and starts looking like fit.&lt;/p&gt;&#xA;&lt;h3 id=&#34;backward-compatibility-is-a-feature-not-a-limitation&#34;&gt;Backward compatibility is a feature, not a limitation&lt;/h3&gt;&#xA;&lt;p&gt;The thing developers complain about — Java&amp;rsquo;s conservatism — is exactly what enterprises are buying.&lt;/p&gt;&#xA;&lt;p&gt;Code written years ago still compiles and runs. Upgrades are usually boring, and boring is the point. When a platform breaks compatibility casually, every upgrade becomes a migration project, and migration projects are where budgets and timelines go to die. Java&amp;rsquo;s discipline here means a large codebase can move forward without being rewritten, and that compounds over the lifespan of a system.&lt;/p&gt;&#xA;&lt;h3 id=&#34;predictable-releases-you-can-plan-around&#34;&gt;Predictable releases you can plan around&lt;/h3&gt;&#xA;&lt;p&gt;Since the six-month release cadence and the Long-Term Support (LTS) model, Java gives organizations something they can actually build a roadmap on. LTS releases give you a stable target with years of support; the interim releases let the language keep evolving without forcing everyone onto the treadmill. You choose your pace. For a business planning multi-year investments, predictability like that is worth more than raw novelty.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-platform-is-still-being-invested-in&#34;&gt;The platform is still being invested in&lt;/h3&gt;&#xA;&lt;p&gt;Java isn&amp;rsquo;t coasting. Recent years have brought genuinely significant work to the platform — virtual threads reshaping how the JVM handles concurrency at scale, ongoing improvements to startup time and memory footprint, pattern matching and records modernizing the language itself, and continued GC advances. This is a thirty-year-old platform still receiving serious, well-funded engineering. That matters when you&amp;rsquo;re betting a decade of software on it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;a-mature-ecosystem-and-a-deep-talent-pool&#34;&gt;A mature ecosystem and a deep talent pool&lt;/h3&gt;&#xA;&lt;p&gt;Two of the most underrated enterprise concerns are &amp;ldquo;what do I build on?&amp;rdquo; and &amp;ldquo;who will maintain it?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;On the first, the JVM ecosystem is vast and battle-tested — Spring and Spring Boot alone cover an enormous surface of what enterprises need, with libraries, tooling, observability, and integration patterns refined over many years of production use.&lt;/p&gt;&#xA;&lt;p&gt;On the second, the Java talent pool is one of the largest in the industry. You can hire for it, you can staff teams at scale, and you can find engineers who&amp;rsquo;ve operated Java systems in production for years. That&amp;rsquo;s not a glamorous advantage, but it&amp;rsquo;s a decisive one when you&amp;rsquo;re maintaining critical systems over a long horizon.&lt;/p&gt;&#xA;&lt;h3 id=&#34;operational-reliability-is-where-it-earns-its-keep&#34;&gt;Operational reliability is where it earns its keep&lt;/h3&gt;&#xA;&lt;p&gt;None of the above would matter if the runtime fell over under load. It doesn&amp;rsquo;t. The JVM&amp;rsquo;s observability, profiling, and tuning story is deep, and the operational patterns for running Java in production are extremely well understood. When something goes wrong at 2 a.m., &amp;ldquo;well understood&amp;rdquo; is exactly the property you want.&lt;/p&gt;&#xA;&lt;h3 id=&#34;where-java-isnt-the-answer&#34;&gt;Where Java isn&amp;rsquo;t the answer&lt;/h3&gt;&#xA;&lt;p&gt;Being honest about the boundaries is what makes the rest credible. If your problem is a small script, a latency-critical systems component, data science and ML research, or a lean startup optimizing for the fastest possible time-to-first-feature, Java is often not the best tool — and you should reach for something that fits. The claim is about a &lt;em&gt;context&lt;/em&gt;, not a coronation.&lt;/p&gt;&#xA;&lt;h3 id=&#34;why-this-is-on-my-mind&#34;&gt;Why this is on my mind&lt;/h3&gt;&#xA;&lt;p&gt;I spend most of my time now building agentic systems on the JVM, and this is precisely why. The interesting, fast-moving part — the reasoning, the model calls, the orchestration — sits on top of a platform I can trust to still be maintainable, staffable, and operable years from now. New capability on a stable foundation is a good trade. It lets me chase what&amp;rsquo;s genuinely new without gambling the boring, load-bearing parts of the system.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-takeaway&#34;&gt;The takeaway&lt;/h3&gt;&#xA;&lt;p&gt;Thirty years in, Java&amp;rsquo;s relevance isn&amp;rsquo;t an accident and it isn&amp;rsquo;t sentiment. It&amp;rsquo;s the payoff of a platform that consistently optimized for maintainability, stability, backward compatibility, predictable evolution, a deep ecosystem, and a large talent pool — the exact concerns that dominate enterprise software.&lt;/p&gt;&#xA;&lt;p&gt;That doesn&amp;rsquo;t make it the best language for everything. It makes it one of the strongest bets you can place when you need systems to run, and keep running, for a very long time.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>🤖 From Prompting to Planning: What Embabel Taught Me About Agents</title>
      <link>https://davidparry.com/blog/2026/07/16/from-prompting-to-planning-what-embabel-taught-me-about-agents/</link>
      <pubDate>Thu, 16 Jul 2026 09:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/07/16/from-prompting-to-planning-what-embabel-taught-me-about-agents/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://davidparry.com/images/goap.svg&#34; alt=&#34;Goal-Oriented Action Planning: typed results accumulate on the blackboard until the goal is reachable&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;I first ran into Embabel at DevNexus and have been quietly using it ever since, but it wasn&amp;rsquo;t until a recent class — going deep with Dashaun — that it really hit home. I spent the last stretch working through an Embabel workshop — building a bounded &amp;ldquo;digital worker&amp;rdquo; that responds to production incidents — and it reorganized how I think about agents on the JVM. Most of the agent content I read treats the LLM as the brain: you write a clever prompt, hand the model some tools, and hope it strings them together. Embabel pushes the intelligence somewhere far more boring and far more trustworthy: into Java types, into a planner, and into policy that a compiler and a test suite can see. This post is my attempt to write down what I actually learned, how I&amp;rsquo;d classify Embabel, and why I&amp;rsquo;m setting aside my own platform, AgentFabric, and starting to use Embabel instead.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;img src=&#34;https://davidparry.com/images/goap.svg&#34; alt=&#34;Goal-Oriented Action Planning: typed results accumulate on the blackboard until the goal is reachable&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;I first ran into Embabel at DevNexus and have been quietly using it ever since, but it wasn&amp;rsquo;t until a recent class — going deep with Dashaun — that it really hit home. I spent the last stretch working through an Embabel workshop — building a bounded &amp;ldquo;digital worker&amp;rdquo; that responds to production incidents — and it reorganized how I think about agents on the JVM. Most of the agent content I read treats the LLM as the brain: you write a clever prompt, hand the model some tools, and hope it strings them together. Embabel pushes the intelligence somewhere far more boring and far more trustworthy: into Java types, into a planner, and into policy that a compiler and a test suite can see. This post is my attempt to write down what I actually learned, how I&amp;rsquo;d classify Embabel, and why I&amp;rsquo;m setting aside my own platform, AgentFabric, and starting to use Embabel instead.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-one-sentence-reframe&#34;&gt;The one-sentence reframe&lt;/h3&gt;&#xA;&lt;p&gt;The line from the workshop that stuck with me was: &lt;em&gt;the worker chooses the path, your code defines the world.&lt;/em&gt; That is the whole shift. In a normal service you write a method that calls four collaborators in a fixed order. In Embabel you declare the capabilities and the desired outcome, and a planner discovers the order at runtime from the current state. You stop writing the sequence and start describing the world the sequence lives in.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-embabel-actually-is&#34;&gt;What Embabel actually is&lt;/h3&gt;&#xA;&lt;p&gt;If I had to classify Embabel in one phrase, I&amp;rsquo;d call it a &lt;strong&gt;neuro-symbolic, planning-first agent framework for the JVM&lt;/strong&gt;. Let me unpack why, because each word is doing work.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s &lt;strong&gt;symbolic&lt;/strong&gt; because the core engine is Goal-Oriented Action Planning (GOAP) — the same technique game AI has used for years. GOAP starts from the goal and reasons about which declared actions, given the current state, can reach it. The planner is deterministic. It is &lt;em&gt;not&lt;/em&gt; the LLM. This is the part people miss: Embabel does not ask a model &amp;ldquo;what should I do next?&amp;rdquo; It computes the plan from types.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s &lt;strong&gt;neural&lt;/strong&gt; because an individual action is free to call a model. The LLM lives &lt;em&gt;inside&lt;/em&gt; a step, boxed in by the types around it, not sitting above the whole process pulling levers.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s &lt;strong&gt;planning-first&lt;/strong&gt; because the mental model is OODA — Observe, Orient, Decide, Act — running as a loop. After every action produces a result (or fails), the planner re-observes the world and reassesses what is now possible. Failure isn&amp;rsquo;t an exception to swallow; it&amp;rsquo;s new information that changes the next plan.&lt;/p&gt;&#xA;&lt;p&gt;And it&amp;rsquo;s &lt;strong&gt;JVM-native&lt;/strong&gt; because Spring still owns everything. Embabel is a Kotlin framework with clean Java authoring, and your agent is still an ordinary &lt;code&gt;@Component&lt;/code&gt;. Constructor injection, interfaces, mocks, tests, observability — none of it changes. Embabel just adds planning metadata on top of methods you&amp;rsquo;d have written anyway.&lt;/p&gt;&#xA;&lt;h3 id=&#34;types-are-preconditions-and-effects&#34;&gt;Types are preconditions and effects&lt;/h3&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the idea that made it click for me. Consider four action signatures from the incident worker:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ServiceObservation &lt;span style=&#34;color:#a6e22e&#34;&gt;observeServices&lt;/span&gt;(IncidentRequest request)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RunbookAssessment &lt;span style=&#34;color:#a6e22e&#34;&gt;applyRunbook&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IncidentRequest request, ServiceObservation observation)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;IncidentResponseReport &lt;span style=&#34;color:#a6e22e&#34;&gt;analyzeIncident&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IncidentRequest request,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ServiceObservation observation,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    RunbookAssessment assessment)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;IncidentWorkflowReport &lt;span style=&#34;color:#a6e22e&#34;&gt;prepareReport&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IncidentRequest request,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ServiceObservation observation,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    RunbookAssessment assessment,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IncidentResponseReport response)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nobody writes the orchestration. The &lt;strong&gt;parameters are the preconditions&lt;/strong&gt; and the &lt;strong&gt;return type is the effect&lt;/strong&gt;. An action is applicable only when every parameter type already exists on the blackboard; running it deposits its return type, which unlocks the next action. So the plan isn&amp;rsquo;t authored — it &lt;em&gt;falls out&lt;/em&gt; of the data flow:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;IncidentRequest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   └─ observeServices ─→ ServiceObservation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        └─ applyRunbook ─→ RunbookAssessment&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             └─ analyzeIncident ─→ IncidentResponseReport&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  └─ prepareReport ─→ IncidentWorkflowReport  (GOAL)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At the start only &lt;code&gt;IncidentRequest&lt;/code&gt; exists, so only &lt;code&gt;observeServices&lt;/code&gt; can fire. Each result makes exactly one more action eligible. I checked this against the workshop&amp;rsquo;s tiny planner, and the mechanism really is that literal: it filters methods whose parameter types are all present, picks the lowest-cost one, invokes it, and puts the result back on the blackboard. That&amp;rsquo;s it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-blackboard-is-typed-working-memory&#34;&gt;The blackboard is typed working memory&lt;/h3&gt;&#xA;&lt;p&gt;There&amp;rsquo;s no JSON router and no stringly-typed state machine. State is a set of domain objects — &lt;code&gt;IncidentRequest&lt;/code&gt;, &lt;code&gt;ServiceObservation&lt;/code&gt;, &lt;code&gt;RunbookAssessment&lt;/code&gt; — that the debugger, the compiler, the tests, the logs, and the planner all see identically. When I&amp;rsquo;ve built agent-ish things in the past, the &amp;ldquo;state&amp;rdquo; was usually a bag of strings passed through prompts, and it was untestable by construction. Making state a typed blackboard means the invariants live in Java, not in prose I&amp;rsquo;m begging a model to respect.&lt;/p&gt;&#xA;&lt;h3 id=&#34;dice-context-is-a-domain-model-not-a-prompt&#34;&gt;DICE: context is a domain model, not a prompt&lt;/h3&gt;&#xA;&lt;p&gt;The workshop calls this DICE — Domain-Integrated Context Engineering — and it&amp;rsquo;s the philosophical core. Instead of stuffing everything into a prompt, you encode organizational knowledge as executable, testable Java &lt;em&gt;first&lt;/em&gt;, then hand the model only the facts it needs to reason inside those boundaries.&lt;/p&gt;&#xA;&lt;p&gt;The clearest example is approval. The runbook, in plain Java, decides whether a proposed production change requires human sign-off:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; (request.&lt;span style=&#34;color:#a6e22e&#34;&gt;incidentType&lt;/span&gt;()) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; OUT_OF_MEMORY &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RunbookAssessment(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Heap pressure is consistent with an OutOfMemory failure.&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        evidence,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Capture a heap dump, roll back the latest risky change, &amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;then validate with a canary.&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);   &lt;span style=&#34;color:#75715e&#34;&gt;// requiresApproval — always&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; HIGH_LATENCY &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RunbookAssessment(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Database timeouts and cache misses indicate dependency saturation.&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        evidence,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Check database and cache health, then use an approved &amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rollback or scale-out.&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The model never sees a &lt;code&gt;requiresApproval&lt;/code&gt; field it could flip. It can&amp;rsquo;t. The model&amp;rsquo;s output type doesn&amp;rsquo;t contain that field, and the goal step copies the deterministic value straight from the runbook. Policy is the compiler&amp;rsquo;s job; interpretation is the model&amp;rsquo;s job. That separation is the whole point.&lt;/p&gt;&#xA;&lt;h3 id=&#34;where-spring-ai-comes-in&#34;&gt;Where Spring AI comes in&lt;/h3&gt;&#xA;&lt;p&gt;Spring AI is the thing doing the actual model call inside an action, and it&amp;rsquo;s exactly where I&amp;rsquo;ve spent most of my own time. The pattern is small and lovely:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; chatClient.&lt;span style=&#34;color:#a6e22e&#34;&gt;prompt&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    You are an SRE following a production runbook.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    Incident: %s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    Metrics: %s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    Logs: %s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    Runbook diagnosis: %s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    Approved strategy: %s&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    Return concise analysis, diagnosis, and recommendation.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    Keep the recommendation inside the approved strategy.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;formatted&lt;/span&gt;(&lt;span style=&#34;color:#75715e&#34;&gt;/* domain facts */&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;call&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;entity&lt;/span&gt;(IncidentResponseReport.&lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;.entity(IncidentResponseReport.class)&lt;/code&gt; is the part I lean on constantly: the model reasons, but Java owns the schema. The reply comes back as a typed object or the action fails — and because it can fail, the plan needs a way to recover.&lt;/p&gt;&#xA;&lt;h3 id=&#34;failure-is-just-another-node-in-the-graph&#34;&gt;Failure is just another node in the graph&lt;/h3&gt;&#xA;&lt;p&gt;This was my favorite lesson, and it&amp;rsquo;s where planning earns its keep. The model call is the &lt;em&gt;preferred&lt;/em&gt; path (low cost). If it throws — Ollama cold, provider down, invalid output — the planner doesn&amp;rsquo;t retry the same prompt and it doesn&amp;rsquo;t ask the model to improvise. It re-observes the blackboard, notices the request and runbook assessment are still true, and selects a &lt;strong&gt;different declared capability&lt;/strong&gt; with the same effect type:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Action&lt;/span&gt;(description &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Fall back to deterministic runbook output&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        readOnly &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, cost &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 10.&lt;span style=&#34;color:#a6e22e&#34;&gt;0&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; IncidentResponseReport &lt;span style=&#34;color:#a6e22e&#34;&gt;fallBackToRunbook&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        IncidentRequest request, RunbookAssessment assessment) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; IncidentResponseReport(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The model was unavailable; deterministic policy was retained.&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        assessment.&lt;span style=&#34;color:#a6e22e&#34;&gt;diagnosis&lt;/span&gt;(),&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        assessment.&lt;span style=&#34;color:#a6e22e&#34;&gt;recommendedAction&lt;/span&gt;());&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two actions, same return type. Cost expresses preference (&lt;code&gt;1.0&lt;/code&gt; for the model, &lt;code&gt;10.0&lt;/code&gt; for the fallback) without a hand-written &lt;code&gt;if/else&lt;/code&gt; route. The blackboard makes the fallback applicable &lt;em&gt;only after&lt;/em&gt; the preferred path fails. Plan repair, expressed as data rather than control flow. And the whole run explains itself afterward: a &lt;code&gt;PlanExecution&lt;/code&gt; record lists completed actions, failed actions, and whether the goal was achieved — an audit trail of decisions and outcomes, not a dump of private chain-of-thought.&lt;/p&gt;&#xA;&lt;h3 id=&#34;autonomy-only-means-anything-inside-boundaries&#34;&gt;Autonomy only means anything inside boundaries&lt;/h3&gt;&#xA;&lt;p&gt;The worker is &lt;em&gt;allowed&lt;/em&gt; to observe Compose, apply the runbook, ask the model, fall back, and prepare a report. It is &lt;em&gt;not allowed&lt;/em&gt; to invent shell commands, restart production, bypass approval, or run forever. Two guardrails enforce the last one: actions are &lt;code&gt;FIRE_ONCE&lt;/code&gt; by default (no silent re-running) and the planner has a hard step limit. A read-only worker that stops at human approval is still autonomous — autonomy is goal-directed selection inside a capability boundary, not unrestricted mutation. That framing alone is worth the price of admission.&lt;/p&gt;&#xA;&lt;h3 id=&#34;why-im-putting-agentfabric-down-and-picking-up-embabel&#34;&gt;Why I&amp;rsquo;m putting AgentFabric down and picking up Embabel&lt;/h3&gt;&#xA;&lt;p&gt;The reason all of this landed so hard is that I&amp;rsquo;ve spent a long time building &lt;a href=&#34;https://github.com/davidparry/AgentFabric&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;AgentFabric&lt;/a&gt;&#xA; — my own durable JVM agent platform on the Spring AI substrate — and Embabel put a name to the philosophy I&amp;rsquo;d been reaching for by feel. Having seen it done properly, I&amp;rsquo;m going to stop investing in AgentFabric and start using Embabel instead.&lt;/p&gt;&#xA;&lt;p&gt;AgentFabric was my attempt to run agents in production, durably, across service boundaries. It stands on:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Spring AI&lt;/strong&gt; as the model and tool layer — &lt;code&gt;ChatClient&lt;/code&gt;, structured-output binding, and MCP tool calling wired through a &lt;code&gt;ToolCallbackProvider&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;LangGraph4j&lt;/strong&gt; for stateful graph topology — nodes that call the model, routers that branch on conditions, and verification loops that re-prompt until output passes a quality gate.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Temporal&lt;/strong&gt; for durable execution — every graph run is a workflow, so a crashed agent resumes from the last completed node instead of replaying LLM calls.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;A2A and MCP&lt;/strong&gt; as the wire protocols — agent-to-agent messaging and agent-to-tool calls.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt; as the source of truth for configuration, checkpoints, and token accounting.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It works. But being honest with myself, most of that is orchestration plumbing I wrote so I could get to the actual agent — and the hardest, most valuable part, the &lt;em&gt;deliberation&lt;/em&gt;, is the part I did worst. In AgentFabric I describe the graph — nodes, edges, routers — as declarative topology, which means I&amp;rsquo;m still hand-authoring the sequence and then maintaining it forever. Embabel&amp;rsquo;s whole point is that I shouldn&amp;rsquo;t be doing that at all: declare capabilities as typed actions and a goal, and the planner &lt;em&gt;derives&lt;/em&gt; the topology from data flow. The centerpiece of my platform turns out to be a worse version of something a maintained framework already gives me for free.&lt;/p&gt;&#xA;&lt;p&gt;What convinced me isn&amp;rsquo;t that Embabel is different — it&amp;rsquo;s that everything I got right in AgentFabric, I got right by accidentally reinventing Embabel, badly:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;My &lt;strong&gt;LangGraph4j state channels&lt;/strong&gt; are a home-grown &lt;strong&gt;blackboard&lt;/strong&gt; — except in Embabel the &lt;em&gt;presence&lt;/em&gt; of a type is what makes the next step applicable, so I stop drawing edges entirely.&lt;/li&gt;&#xA;&lt;li&gt;My &lt;strong&gt;verifier loop plus deterministic fallback&lt;/strong&gt; is hand-wired &lt;strong&gt;plan repair&lt;/strong&gt; — Embabel does it with a second declared action at a higher cost, selected automatically when the model action fails. No re-prompt path to maintain.&lt;/li&gt;&#xA;&lt;li&gt;My &lt;strong&gt;&lt;code&gt;completeAs(Class&amp;lt;T&amp;gt;)&lt;/code&gt;&lt;/strong&gt; calls are just Embabel&amp;rsquo;s &lt;strong&gt;&lt;code&gt;.entity(...)&lt;/code&gt;&lt;/strong&gt; — both are Spring AI underneath, so this is the one place there&amp;rsquo;s genuinely nothing to migrate; it&amp;rsquo;s the same call.&lt;/li&gt;&#xA;&lt;li&gt;My &lt;strong&gt;budget guard&lt;/strong&gt; router is a clumsier &lt;strong&gt;cost-based action preference&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;My &lt;strong&gt;guarded write tools&lt;/strong&gt; (dry-run unless &lt;code&gt;apply=true&lt;/code&gt;) are the same instinct as keeping &lt;code&gt;requiresApproval&lt;/code&gt; in Java, never the model — one thing I&amp;rsquo;ll happily carry over as a habit rather than a codebase.&lt;/li&gt;&#xA;&lt;li&gt;Even the &lt;strong&gt;durability&lt;/strong&gt; I reached for Temporal to get is largely native: Embabel&amp;rsquo;s blackboard persists through a pluggable &lt;code&gt;AgentProcessRepository&lt;/code&gt; — in-memory by default, but back it with &lt;strong&gt;PostgreSQL or MongoDB&lt;/strong&gt; and a process survives a restart. And &lt;strong&gt;human-in-the-loop pause/resume&lt;/strong&gt; is built in via awaitables: a process returns &lt;code&gt;WAITING&lt;/code&gt; with a &lt;code&gt;processId&lt;/code&gt; and you resume it later through &lt;code&gt;/continue&lt;/code&gt;. I wrote a Temporal integration to get exactly these two things.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;There&amp;rsquo;s a real cost to admitting this — AgentFabric is a lot of my work — but continuing to maintain a bespoke orchestration engine to avoid adopting a better, supported one is just ego with a build file. Embabel gives me the planner; Spring gives me the beans; Spring AI gives me the model calls I already knew; a persistent repository gives me durable state and pause/resume.&lt;/p&gt;&#xA;&lt;p&gt;So do I still need Temporal? Almost never. The &lt;strong&gt;only&lt;/strong&gt; case that would pull it back in is when I need guarantees Embabel&amp;rsquo;s step-level persistence doesn&amp;rsquo;t offer: deterministic &lt;strong&gt;replay with exactly-once activities&lt;/strong&gt; (the JVM dies mid-LLM-call and must resume &lt;em&gt;without&lt;/em&gt; re-invoking the model), &lt;strong&gt;durable timers&lt;/strong&gt; (&amp;ldquo;wait three days, then continue&amp;rdquo;), or &lt;strong&gt;distributed orchestration&lt;/strong&gt; across a worker fleet with guaranteed delivery and backoff. For a single-process agent that plans, calls a model, and pauses for human approval — which is most of what I actually build — none of that applies. So AgentFabric goes on the shelf, and my next agent starts as an Embabel &lt;code&gt;@Agent&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;And with the recent news that Embabel is heading for a &lt;code&gt;1.0.0&lt;/code&gt; release, whatever hesitation I had about betting on it is gone. I&amp;rsquo;m all in.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-im-taking-away&#34;&gt;What I&amp;rsquo;m taking away&lt;/h3&gt;&#xA;&lt;p&gt;The move Embabel names is the move from &lt;strong&gt;calling APIs to building workers&lt;/strong&gt;. A worker navigates a typed domain, invokes real Spring-managed services, plans from preconditions and effects, stops at an explicit goal, repairs a failed path, preserves human approval, and leaves an audit trail. None of that requires trusting a model with the steering wheel. It requires giving the model a small, well-lit room to think in — and letting typed code define everything outside the door. That&amp;rsquo;s the bet I&amp;rsquo;m making by setting my own platform down and building on Embabel instead.&lt;/p&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re on the JVM and you&amp;rsquo;ve been building agents by growing ever-larger prompts, I&amp;rsquo;d genuinely recommend sitting with GOAP for an afternoon. It reframes the problem from &amp;ldquo;how do I make the model behave&amp;rdquo; to &amp;ldquo;what world am I asking it to operate in&amp;rdquo; — and the second question is one your compiler can help you answer.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>📱 My Mobile Applications</title>
      <link>https://davidparry.com/blog/2024/05/19/my-mobile-applications/</link>
      <pubDate>Sun, 19 May 2024 08:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2024/05/19/my-mobile-applications/</guid>
      <description>&lt;h3 id=&#34;github-projects&#34;&gt;GitHub Projects&lt;/h3&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/davidparry/DavidWidgets&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;David Widgets an Android Library of UI Widgets&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/davidparry/DavidWidgetsShowcase&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Example Android Project showing how to use the widgets from the DavidWidgets Library&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;app-store-listings&#34;&gt;App Store Listings&lt;/h3&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://play.google.com/store/apps/developer?id=David&amp;#43;Parry&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;PlayStore&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://apps.apple.com/us/developer/david-parry/id1555866076&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;App Store&lt;/a&gt;&#xA;&lt;/p&gt;</description>
      <content:encoded>&lt;h3 id=&#34;github-projects&#34;&gt;GitHub Projects&lt;/h3&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/davidparry/DavidWidgets&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;David Widgets an Android Library of UI Widgets&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/davidparry/DavidWidgetsShowcase&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Example Android Project showing how to use the widgets from the DavidWidgets Library&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;app-store-listings&#34;&gt;App Store Listings&lt;/h3&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://play.google.com/store/apps/developer?id=David&amp;#43;Parry&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;PlayStore&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://apps.apple.com/us/developer/david-parry/id1555866076&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;App Store&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>📝 Why Choose Hugo for Static Content?</title>
      <link>https://davidparry.com/blog/2024/05/18/why-choose-hugo-for-static-content/</link>
      <pubDate>Sat, 18 May 2024 16:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2024/05/18/why-choose-hugo-for-static-content/</guid>
      <description>&lt;p&gt;As a developer, my primary focus is on solving unique and challenging problems that have not yet been addressed. Writing my own static content generation system might seem appealing, but it would consume a significant amount of time and effort that could be better spent on more complex tasks. Hugo is a mature, robust, and well-supported static site generator that allows me to focus on what truly matters: innovating and solving new problems.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;As a developer, my primary focus is on solving unique and challenging problems that have not yet been addressed. Writing my own static content generation system might seem appealing, but it would consume a significant amount of time and effort that could be better spent on more complex tasks. Hugo is a mature, robust, and well-supported static site generator that allows me to focus on what truly matters: innovating and solving new problems.&lt;/p&gt;&#xA;&lt;p&gt;Hugo provides a highly efficient and flexible framework for creating static content. It is designed to handle a wide range of scenarios with ease, offering features such as Markdown support, custom templates, and shortcodes. These capabilities allow me to quickly and efficiently generate static content without having to reinvent the wheel. By leveraging Hugo, I can avoid the pitfalls of maintaining a custom-built system and instead rely on a proven solution that is constantly being improved by a large community of developers.&lt;/p&gt;&#xA;&lt;p&gt;My requirements for static content generation are straightforward and do not warrant the development of a custom system. Hugo excels in delivering exactly what I need with minimal configuration and setup. It enables rapid deployment, supports a variety of content types, and integrates seamlessly with modern development workflows. By choosing Hugo, I ensure that my static content generation process is reliable, maintainable, and future-proof, freeing me to tackle the more complex and unique challenges in my projects.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>👨‍💻 Codebase Resurrection: Given at JavaMUG </title>
      <link>https://davidparry.com/blog/2023/10/10/codebase-resurrection-given-at-javamug/</link>
      <pubDate>Tue, 10 Oct 2023 19:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2023/10/10/codebase-resurrection-given-at-javamug/</guid>
      <description>&lt;p&gt;Welcome to this Java conference session, where we delve into the transformative power of AI in refactoring and improving codebases. If your codebase is in dire need of a revamp and you&amp;rsquo;re grappling with complex, tangled code, this session is tailored for you. We&amp;rsquo;ll explore how Codium.AI can serve as your secret weapon, helping you tackle subpar code effectively.&lt;/p&gt;&#xA;&lt;h3 id=&#34;understanding-code-health&#34;&gt;Understanding Code Health:&lt;/h3&gt;&#xA;&lt;h4 id=&#34;analyzing-code-health&#34;&gt;Analyzing Code Health:&lt;/h4&gt;&#xA;&lt;p&gt;We demonstrated how Codium.AI can assess the current state of your codebase, identifying areas that need improvement. This involves evaluating code complexity, detecting code smells, and highlighting technical debt.&#xA;Visualizing Code Issues: Codium.AI provides detailed reports and visualizations, making it easier to understand the intricacies of your code and prioritize refactoring efforts.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;Welcome to this Java conference session, where we delve into the transformative power of AI in refactoring and improving codebases. If your codebase is in dire need of a revamp and you&amp;rsquo;re grappling with complex, tangled code, this session is tailored for you. We&amp;rsquo;ll explore how Codium.AI can serve as your secret weapon, helping you tackle subpar code effectively.&lt;/p&gt;&#xA;&lt;h3 id=&#34;understanding-code-health&#34;&gt;Understanding Code Health:&lt;/h3&gt;&#xA;&lt;h4 id=&#34;analyzing-code-health&#34;&gt;Analyzing Code Health:&lt;/h4&gt;&#xA;&lt;p&gt;We demonstrated how Codium.AI can assess the current state of your codebase, identifying areas that need improvement. This involves evaluating code complexity, detecting code smells, and highlighting technical debt.&#xA;Visualizing Code Issues: Codium.AI provides detailed reports and visualizations, making it easier to understand the intricacies of your code and prioritize refactoring efforts.&lt;/p&gt;&#xA;&lt;h3 id=&#34;ai-powered-testing&#34;&gt;AI-Powered Testing:&lt;/h3&gt;&#xA;&lt;h4 id=&#34;automated-testing&#34;&gt;Automated Testing:&lt;/h4&gt;&#xA;&lt;p&gt;We showcased Codium.AI&amp;rsquo;s capability to generate and run AI-driven tests, ensuring comprehensive coverage and identifying potential bugs before they become issues.&#xA;Confidence in Refactoring: By leveraging AI-powered testing, developers can refactor code with confidence, knowing that the AI will catch any unintended side effects or bugs introduced during the process.&#xA;Precision Refactoring:&lt;/p&gt;&#xA;&lt;h4 id=&#34;expert-recommended-refactorings&#34;&gt;Expert-Recommended Refactorings:&lt;/h4&gt;&#xA;&lt;p&gt;Codium.AI incorporates best practices from industry experts, suggesting precise refactorings to simplify complex code structures and enhance maintainability.&#xA;Step-by-Step Guidance: The tool provides step-by-step guidance on applying refactorings, ensuring that developers can implement changes efficiently and accurately.&#xA;Measuring Success:&lt;/p&gt;&#xA;&lt;h4 id=&#34;evaluating-impact&#34;&gt;Evaluating Impact:&lt;/h4&gt;&#xA;&lt;p&gt;We discussed how to use Codium.AI to measure the impact of refactorings, tracking improvements in code quality, performance, and maintainability.&#xA;Continuous Improvement: The tool supports continuous monitoring and improvement, helping teams maintain high standards of code quality over time.&lt;/p&gt;&#xA;&lt;h4 id=&#34;interactive-session&#34;&gt;Interactive Session:&lt;/h4&gt;&#xA;&lt;p&gt;The session was highly interactive, with over 30 attendees actively participating. Attendees asked insightful questions about the practical application of Codium.AI in various scenarios, and we provided detailed answers and demonstrations.&lt;/p&gt;&#xA;&lt;h3 id=&#34;case-study&#34;&gt;Case Study:&lt;/h3&gt;&#xA;&lt;p&gt;We shared a real-world example from our team, highlighting how Codium.AI worked like a charm in revitalizing a particularly challenging codebase. The tool&amp;rsquo;s AI-driven insights and recommendations proved invaluable, making the refactoring process efficient and effective.&lt;/p&gt;&#xA;&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion:&lt;/h3&gt;&#xA;&lt;p&gt;By the end of the session, attendees were equipped with the knowledge and tools to harness the power of Codium.AI, transforming their codebases into efficient, robust, and future-proof systems. The positive feedback and high level of engagement reflected the value and potential of integrating AI into coding practices.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://davidparry.com/images/codebase_res.jpeg&#34; alt=&#34;img.png&#34;&gt;&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>💻 Redefining Error Monitoring: Breaking Free from Brittle Logs</title>
      <link>https://davidparry.com/blog/2023/03/18/redefining-error-monitoring-breaking-free-from-brittle-logs/</link>
      <pubDate>Sat, 18 Mar 2023 16:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2023/03/18/redefining-error-monitoring-breaking-free-from-brittle-logs/</guid>
      <description>&lt;p&gt;In recent years, the landscape of error monitoring has undergone a significant transformation. Traditional log-based approaches, while invaluable for root cause analysis, have shown their brittleness in the face of evolving software applications. However, the good news is that it&amp;rsquo;s now easier than ever to break free from the shackles of brittle logs and adopt a more reliable error monitoring strategy. This transformation has been made possible by tools like Micrometer, a powerful metrics collection library, and AWS EventBridge, a versatile event bus, each offering distinct approaches to error monitoring.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;In recent years, the landscape of error monitoring has undergone a significant transformation. Traditional log-based approaches, while invaluable for root cause analysis, have shown their brittleness in the face of evolving software applications. However, the good news is that it&amp;rsquo;s now easier than ever to break free from the shackles of brittle logs and adopt a more reliable error monitoring strategy. This transformation has been made possible by tools like Micrometer, a powerful metrics collection library, and AWS EventBridge, a versatile event bus, each offering distinct approaches to error monitoring.&lt;/p&gt;&#xA;&lt;p&gt;Micrometer has emerged as a game-changer in the world of error monitoring by providing developers with a simple and consistent way to emit custom metrics and events from their applications. Spring Boot, a popular framework in the Java ecosystem, has seamlessly integrated with Micrometer, making it incredibly easy for developers to harness its capabilities. By leveraging Micrometer, you can emit distinct error events, separate from your regular logs, that are designed to be stable and reliable. These events serve as beacons, allowing your monitoring and alerting systems to pinpoint critical issues, even as your application evolves.&lt;/p&gt;&#xA;&lt;p&gt;On the other hand, AWS EventBridge offers an alternative approach to error monitoring. It acts as a centralized event bus that seamlessly connects various AWS services and third-party tools. By emitting error events to EventBridge, you can effortlessly integrate with a wide range of monitoring and alerting tools, such as AWS CloudWatch, Datadog, or even custom applications. This means that not only can you reliably capture errors, but you can also set up sophisticated workflows and responses based on these events. The result is a monitoring ecosystem that&amp;rsquo;s both robust and flexible, adapting to your specific needs and evolving alongside your application.&lt;/p&gt;&#xA;&lt;p&gt;In conclusion, the days of relying solely on brittle logs for error monitoring are behind us. Thanks to tools like Micrometer and AWS EventBridge, error monitoring has become more accessible and reliable than ever. Whether you choose the integrated simplicity of Micrometer with Spring Boot or the versatility of AWS EventBridge, you can ensure that your application&amp;rsquo;s critical issues are detected and acted upon, regardless of how your application evolves. It&amp;rsquo;s a transformation that empowers developers to build more resilient and responsive software systems while gaining deeper insights into application behavior.&lt;/p&gt;&#xA;</content:encoded>
    </item>
  </channel>
</rss>
