<?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>Development on David Parry</title>
    <link>https://davidparry.com/tags/development/</link>
    <description>Recent content in Development on David Parry</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 14 Aug 2026 15:00:00 -0500</lastBuildDate>
    <atom:link href="https://davidparry.com/tags/development/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>🖥️ You Don&#39;t Need a Frontier Model. You Need a Spec.</title>
      <link>https://davidparry.com/blog/2026/08/14/%EF%B8%8F-you-dont-need-a-frontier-model.-you-need-a-spec./</link>
      <pubDate>Fri, 14 Aug 2026 15:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/08/14/%EF%B8%8F-you-dont-need-a-frontier-model.-you-need-a-spec./</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/you-dont-need-a-frontier-model-linkedin.png&#34; alt=&#34;A laptop on a desk running a local spec-driven loop: a spec-tests-code triangle on one side, a local machine on the other, and a test bar going from red to green&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;You do not need a frontier model to build professional software. You need a spec, a test bar, and a workflow that will not let anyone — human or model — skip either.&lt;/strong&gt; Frontier models are what you reach for when you want day-zero results: something that compiles this afternoon and looks finished until you have to live with it. A responsible solution is slower in the screenshot and faster over the life of the system. The surprise of the last year, for me, is that this path is now cheap enough to run on a laptop.&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/you-dont-need-a-frontier-model-linkedin.png&#34; alt=&#34;A laptop on a desk running a local spec-driven loop: a spec-tests-code triangle on one side, a local machine on the other, and a test bar going from red to green&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;You do not need a frontier model to build professional software. You need a spec, a test bar, and a workflow that will not let anyone — human or model — skip either.&lt;/strong&gt; Frontier models are what you reach for when you want day-zero results: something that compiles this afternoon and looks finished until you have to live with it. A responsible solution is slower in the screenshot and faster over the life of the system. The surprise of the last year, for me, is that this path is now cheap enough to run on a laptop.&lt;/p&gt;&#xA;&lt;p&gt;I already wrote that &lt;a href=&#34;https://davidparry.com/blog/2026/08/07/spec-first-was-always-right-agents-just-made-it-fast/&#34;&gt;spec-first was always right, and that agents finally made it fast&lt;/a&gt;&#xA;. That post was the argument. This one is what I learned once I stopped treating the hosted model as the product and started treating a local model as one tool inside a spec-driven loop. The working proof is the &lt;a href=&#34;https://github.com/davidparry/tdd-bdd-agentic&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;bdd&lt;/code&gt; CLI&lt;/a&gt;&#xA; that grew out of that workshop — one native binary, &lt;a href=&#34;https://ollama.com&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Ollama&lt;/a&gt;&#xA; by default, no cloud calls, no token meter.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-cli-is-the-discipline-compiled&#34;&gt;The CLI is the discipline, compiled&lt;/h2&gt;&#xA;&lt;p&gt;The workshop needed an MCP server so an agent could not wander. The CLI is that same loop as a program you run yourself. The &lt;a href=&#34;https://davidparry.github.io/tdd-bdd-agentic/manual/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;command manual&lt;/a&gt;&#xA; is the full surface; the idea is one sentence:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;The requirements spec is the source of truth, and the discipline is enforced by tooling, not by convention.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Everything else follows from that. The spec is machine-validated (&lt;code&gt;bdd spec validate&lt;/code&gt;) and its wording is quality-gated (&lt;code&gt;bdd spec refine&lt;/code&gt;) before any scenario or line of production code exists. A valid-but-vague requirement is caught and reworded first. Behavior flows downhill from the approved spec: Gherkin scenarios tagged with requirement ids, step definitions, unit tests, and only then production code. The Red/Green/Refactor cycle is a state machine, not a suggestion — &lt;code&gt;start_refactor&lt;/code&gt; is refused on a red bar, and a requirement is only marked implemented behind a green one.&lt;/p&gt;&#xA;&lt;p&gt;Agents get no escape hatches. No arbitrary file writes, no shell, no &amp;ldquo;just install this for me.&amp;rdquo; Every mutation goes through a typed, validated tool, lands in staging (&lt;code&gt;.bdd-staged/&lt;/code&gt;), and waits for a human to review it. The same tools serve a person at a prompt and an agent over MCP. The LLM is local, discovered rather than assumed, and generation falls back to deterministic templates when Ollama is down or empty. Nothing is ever installed for you.&lt;/p&gt;&#xA;&lt;p&gt;Run bare &lt;code&gt;bdd&lt;/code&gt; and you get the loop, the version, and whatever local model is already on the machine:&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;$ bdd&#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;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  │    &amp;gt; bdd  v0.2.4                 │&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  │    spec → RED → GREEN → REFACTOR │&#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;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Model set for this session: qwen3-coder-next:q4_K_M (not saved - keep it with: bdd model use qwen3-coder-next:q4_K_M).&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bdd&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From an empty directory, &lt;code&gt;bdd greenfield&lt;/code&gt; runs the whole creation order with exactly two human gates: the wording of the driving spec, and the review of generated tests before they are committed. From an existing project, you drive the same phases yourself — &lt;code&gt;spec draft&lt;/code&gt;, &lt;code&gt;spec validate&lt;/code&gt;, &lt;code&gt;spec refine&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;implement&lt;/code&gt;, &lt;code&gt;refactor&lt;/code&gt; — and &lt;code&gt;bdd status&lt;/code&gt; names the one next step that actually moves the loop forward.&lt;/p&gt;&#xA;&lt;p&gt;The model is not sitting above this process. It is boxed inside a few commands: drafting a requirement from plain words, polishing step definitions and unit tests, attempting an implementation against a recorded RED bar. &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, and &lt;code&gt;refactor&lt;/code&gt; never call it. That is not a prompt instruction. It is the architecture.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-i-actually-learned-about-local-models&#34;&gt;What I actually learned about local models&lt;/h2&gt;&#xA;&lt;p&gt;The lesson is not &amp;ldquo;an 8B model beats Opus.&amp;rdquo; The lesson is: &lt;strong&gt;once you move the deterministic work out of the model, a local model becomes enough.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;I measured a version of that claim in &lt;a href=&#34;https://davidparry.com/blog/2026/07/20/skills-vs-mcp-what-the-token-bill-actually-measures/&#34;&gt;Skills vs MCP&lt;/a&gt;&#xA;. For one workload, putting the business rules in code instead of in the model&amp;rsquo;s head cut hosted cost by 32% on Claude Opus 4.8 and 76% on GPT-5. The same implementation on &lt;code&gt;qwen3:30b&lt;/code&gt;, served locally through Ollama on an Apple M4 Max, cost nothing and dropped mean latency from 43.4 seconds (instruction-only Skill) and 54.4 seconds (prompt only) to 10.8 seconds with the tool. There is no per-token invoice to blame on that last comparison. Generating fewer tokens still consumed less wall-clock time on my hardware.&lt;/p&gt;&#xA;&lt;p&gt;The CLI takes that architecture and applies it to the development loop itself. Spec validation is code. Wording critique is code. The TDD state machine is code. Gherkin parsing is code. Test execution is Maven, cucumber-js, &lt;code&gt;dotnet test&lt;/code&gt;, or &lt;code&gt;cargo test&lt;/code&gt; — the project&amp;rsquo;s own runner, not a model&amp;rsquo;s impression of a runner. The model is asked to propose, not to be the system of record. When it proposes a step definition, the CLI prefers that output only if it validates; otherwise the deterministic template ships. That is why &lt;code&gt;qwen3-coder-next:q4_K_M&lt;/code&gt; is a reasonable default on this tool, and why &lt;code&gt;qwen3:30b&lt;/code&gt; is a luxury rather than a requirement.&lt;/p&gt;&#xA;&lt;p&gt;Open-weight coding models have also closed enough of the raw-capability gap that this is no longer a thought experiment. Alibaba&amp;rsquo;s &lt;a href=&#34;https://qwenlm.github.io/blog/qwen3-coder/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Qwen3-Coder&lt;/a&gt;&#xA; reports state-of-the-art results among open models on SWE-Bench Verified without test-time scaling, and describes the 480B variant as comparable to Claude Sonnet 4 on agentic coding. You can argue with any one leaderboard. You cannot argue with the direction: the model you can run next to the repo is no longer a toy, and the remaining gap is exactly where unconstrained agent loops still fall down — long-horizon, multi-file, &amp;ldquo;figure out what I meant&amp;rdquo; work. Spec-driven development is how you stop asking the model to do that work.&lt;/p&gt;&#xA;&lt;p&gt;METR&amp;rsquo;s &lt;a href=&#34;https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;early-2025 randomized trial&lt;/a&gt;&#xA; is the other data point I keep coming back to. Experienced open-source developers working on their own mature repositories, using frontier tools of the day (primarily Cursor with Claude), took 19% longer when AI was allowed. They expected a 24% speedup. After living through the slowdown, they still believed they had been sped up by 20%. Perception and the clock disagreed. METR was careful about what that does &lt;em&gt;not&lt;/em&gt; prove. They said, explicitly, that they do not provide evidence &amp;ldquo;there are not ways of using existing AI systems more effectively&amp;rdquo; — scaffolding, prompting, repository-specific context. Their later &lt;a href=&#34;https://metr.org/blog/2026-02-24-uplift-update/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;2026 follow-up&lt;/a&gt;&#xA; ran into a different problem: too many developers now refuse to work without AI, so the experiment can no longer see the tasks where people expect the biggest lift. I read both results the same way. Unconstrained generation on a real codebase is not the same activity as a gated spec-to-green loop. The first can feel fast and still be slow. The second is slower to start and cheaper to finish, and it does not require the most expensive model on the market.&lt;/p&gt;&#xA;&lt;h2 id=&#34;day-zero-is-a-product-professional-software-is-a-process&#34;&gt;Day-zero is a product. Professional software is a process.&lt;/h2&gt;&#xA;&lt;p&gt;I will say this as cleanly as I can. Frontier models are extraordinary at day-zero. You describe a thing, files appear, a demo boots, a screenshot looks like a product. That is a real capability, and it is the capability the labs demo, because it is the capability that converts. If what you wanted was a prototype before lunch, pay for the tokens. I do that too.&lt;/p&gt;&#xA;&lt;p&gt;Professional software is a different shape. Someone has to write down what &amp;ldquo;done&amp;rdquo; means in a form that can be checked. Tests have to fail for the right reason before they pass for the right reason. The code has to be the simplest thing that makes that true, and then it has to be reviewed. Edge cases have to be found on purpose, not stumbled into in production. That work has been preached at us for as long as I have been doing this — test-first, behavior-first, requirements-first. Cucumber and Gherkin were the first tools that let the requirement itself become executable, and I never went back. What changed is not the advice. What changed is that an agent will now execute the advice if you put it in a tool instead of a slide.&lt;/p&gt;&#xA;&lt;p&gt;GitClear&amp;rsquo;s &lt;a href=&#34;https://www.gitclear.com/ai_assistant_code_quality_2025_research/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;2025 look at 211 million changed lines&lt;/a&gt;&#xA; — Google, Microsoft, Meta, and enterprise C-corps, 2020 through 2024 — is what day-zero looks like when it becomes the default. Lines associated with refactoring fell from 25% of changed code in 2021 to less than 10% in 2024. Copy/pasted lines rose from 8.3% to 12.3% in the same window, and 2024 was the first year in their dataset where copy/paste exceeded moved (refactored) code. Assistants do beget more lines. Senior developers, asked what would unlock their team, do not answer &amp;ldquo;more lines.&amp;rdquo; They answer reuse, tests, and the courage to change old code. Those are the habits a day-zero loop does not practice, because they do not show up in the demo.&lt;/p&gt;&#xA;&lt;p&gt;GitHub putting a name and a toolkit on this — &lt;a href=&#34;https://github.com/github/spec-kit&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Spec Kit&lt;/a&gt;&#xA;, &amp;ldquo;define what to build before building it&amp;rdquo; — is the industry catching up to a sentence some of us have been repeating for twenty years. I am glad it exists. Specs that live as markdown for an agent to interpret are still a step up from a vibe. They are not the same as a spec that is structurally validated, wording-gated, and tied to a real Cucumber suite and a TDD state machine that will refuse to refactor on red. That difference is why &lt;code&gt;bdd&lt;/code&gt; exists. The closest tools I know of each do part of this. None of them combine a machine-validated spec, a wording gate, real Cucumber across Java, JavaScript/TypeScript, .NET, and Rust, an enforced test-state machine, typed mutations with no shell escape hatch, an embedded MCP server, and a local-only LLM, in one binary.&lt;/p&gt;&#xA;&lt;h2 id=&#34;it-does-not-behoove-them-to-teach-you-this&#34;&gt;It does not behoove them to teach you this&lt;/h2&gt;&#xA;&lt;p&gt;I want to be as careful here as I was in the token-bill post. This is an incentive, not a conspiracy. Model providers have good reasons to make models more capable, and customers are free to buy that capability. Still, the bill is not subtle. On the two hosted models I priced, output tokens cost five to eight times input tokens. When a model does more work, customers usually buy more inference.&lt;/p&gt;&#xA;&lt;p&gt;It does not behoove a frontier lab to teach you a workflow in which an 8B local model drafts a requirement, a validator rejects the sloppy wording, a test runner produces a red bar, and the model is only then allowed to attempt the smallest patch that turns it green. That workflow spends its calories in code you already own. It does not spend them on a metered API. The labs need the habit: open the chat, describe a feeling, accept a tree of files, come back when it breaks. Addiction is an ugly word for a pricing model, but the loop is the same shape. You stay because the first screenshot was free in time and expensive in everything that came after.&lt;/p&gt;&#xA;&lt;p&gt;I do not think the models are the enemy. I think the missing guidance is the enemy, and the missing guidance is the same guidance we have had since before any of this: write the spec, write the test, write the code last, keep all three in sync. Frontier models do not ship with that. A CLI can.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-rehire-articles-are-a-symptom-they-are-not-the-point&#34;&gt;The rehire articles are a symptom. They are not the point.&lt;/h2&gt;&#xA;&lt;p&gt;I have seen the articles, and the videos, about companies hiring people back after betting that AI would replace them. Some of that reporting is looser than it sounds. The number that is actually sourced is not &amp;ldquo;developers,&amp;rdquo; and I am not going to pretend it is.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.gartner.com/en/newsroom/press-releases/2026-02-03-gartner-predicts-half-of-companies-that-cut-customer-service-staff-due-to-ai-will-rehire-by-2027&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Gartner&amp;rsquo;s February 2026 forecast&lt;/a&gt;&#xA; is about customer service. By 2027, they say, 50% of companies that attributed headcount reduction to AI will rehire staff to perform similar functions, under different job titles. Their own October 2025 survey of 321 customer service and support leaders found that only 20% had actually reduced agent staffing because of AI. Kathy Ross, a Gartner analyst on that practice, said most recent workforce reductions were influenced by broader economic conditions rather than automation alone. Emily Potosky, in the same release: &amp;ldquo;AI simply isn&amp;rsquo;t mature enough to fully replace the expertise, empathy, and judgment that human agents provide.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Klarna is the case everyone uses, and it is also customer service. In May 2025, CEO Sebastian Siemiatkowski &lt;a href=&#34;https://www.bloomberg.com/news/articles/2025-05-08/klarna-turns-from-ai-to-real-person-customer-service&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;told Bloomberg&lt;/a&gt;&#xA; that cost had been too predominant a factor, and that what you end up with is lower quality. He started recruiting so customers could always speak to a person. That is not &amp;ldquo;AI failed, delete the chatbot.&amp;rdquo; It is &amp;ldquo;we over-indexed on the demo, and the demo was not the job.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;IBM is moving the other direction on purpose. CHRO Nickle LaMoreaux said the company plans to &lt;a href=&#34;https://www.ibm.com/think/news/entry-level-roles-get-reset-ai&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;triple US entry-level hiring in 2026&lt;/a&gt;&#xA;, across software and the rest of the business, because &amp;ldquo;if we don&amp;rsquo;t continue to invest in entry-level hires, what happens in 3–5 years? There&amp;rsquo;s no pipeline; the well simply dries up.&amp;rdquo; That is not a rehire of the old org chart. It is an admission that if you delete the bottom of the profession, you do not get a more efficient profession. You get a cliff.&lt;/p&gt;&#xA;&lt;p&gt;The engineering version of this story is not a Gartner headcount number I can cite. It is the GitClear chart: more lines, less reuse, more churn, code that is written to be shipped today and expensive to touch tomorrow. Companies that staffed for day-zero generation and starved the people who can tell a spec from a vibe will hire some of those people back. Of course they will.&lt;/p&gt;&#xA;&lt;p&gt;If anyone who knows me is watching that wave, I hope what comes back is only the 30%. I hope the 70% are now out of the equation. I am going to leave the 70–20–9–1 split to another post. Trust me, it will offend a lot of people. It is not meant to. It is about priorities, and about other aspects of life, and about whether filling a seat was ever the right use of someone&amp;rsquo;s years. If this era does one decent thing, it will be to stop asking the 70% to occupy a spot they were never going to love, and to let them go explore the rest of a life. That is only possible if the 20% take the AI and use it for good — not to manufacture day-zero code, and not to skip the techniques that have been taught for as long as I have been doing this. The 20% have to keep the spec, the tests, and the code in sync. Nobody else is going to do that for them. The frontier model will not. It is not in its interest.&lt;/p&gt;&#xA;&lt;h2 id=&#34;try-it-on-the-machine-you-already-have&#34;&gt;Try it on the machine you already have&lt;/h2&gt;&#xA;&lt;p&gt;Clone the &lt;a href=&#34;https://github.com/davidparry/tdd-bdd-agentic&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;repo&lt;/a&gt;&#xA;, or install &lt;code&gt;bdd&lt;/code&gt; from the &lt;a href=&#34;https://github.com/davidparry/tdd-bdd-agentic/releases/latest&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;latest release&lt;/a&gt;&#xA;. Install &lt;a href=&#34;https://ollama.com&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Ollama&lt;/a&gt;&#xA; if you do not have it, pull something small, and point the CLI at it:&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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ollama pull qwen3-coder-next:q4_K_M&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bdd model use qwen3-coder-next:q4_K_M&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir calculator &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cd calculator&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bdd greenfield&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Describe a calculator in a sentence. Watch a local model split that sentence into a requirement. Watch &lt;code&gt;validate_spec&lt;/code&gt; and &lt;code&gt;refine_requirement&lt;/code&gt; refuse the sloppy wording. Approve the spec when it is actually the behavior you want. Watch the bar go RED, then GREEN. You will not have called a frontier API. You will have practiced the same discipline I have been arguing for for twenty years, at the speed an agent can finally sustain.&lt;/p&gt;&#xA;&lt;p&gt;To learn more about Spec-Driven Development — the workshop, the CLI, the talk, and how the loop actually runs — start at &lt;a href=&#34;https://davidparry.github.io/tdd-bdd-agentic/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;davidparry.github.io/tdd-bdd-agentic&lt;/a&gt;&#xA;.&lt;/p&gt;&#xA;&lt;p&gt;That is the whole claim. Frontier models are optional. The spec is not. Day-zero is a screenshot. Software is what is still true after the screenshot.&lt;/p&gt;&#xA;</content:encoded>
    </item>
    <item>
      <title>🔺 Spec-First Was Always Right — Agents Just Made It Fast</title>
      <link>https://davidparry.com/blog/2026/08/07/spec-first-was-always-right-agents-just-made-it-fast/</link>
      <pubDate>Fri, 07 Aug 2026 16:00:00 -0500</pubDate>
      <guid>https://davidparry.com/blog/2026/08/07/spec-first-was-always-right-agents-just-made-it-fast/</guid>
      <description>&lt;img src=&#34;https://davidparry.com/images/tdd-in-the-agentic-era-spec-driven-convert-linkedin.png&#34; alt=&#34;The Specs, Code, Tests triangle kept in sync — an AI agent turns the crank while a human steers, and the test bar goes from red to green&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;For twenty years I have argued that requirements, tests, and code are the same information at three altitudes, and that the job is to keep them in sync. The argument has not changed — the economics finally have.&lt;/strong&gt; The correct way was always to nail the spec first, but that was the slower way, and when a human sits down at a keyboard there has always been an unspoken rule that whatever they type had better be code. Spec-writing looked like stalling. So teams typed the code, and the spec — if it ever existed — drifted into fiction. That trade-off is now dead. With an agent doing the transformation from requirement to test to implementation, the spec-first path is no longer the slow path. Getting it right is now &lt;em&gt;faster&lt;/em&gt; than winging it, because a well-captured requirement is the thing an agent can actually execute against, and a vague one is the thing you pay for in review, rework, and slop.&lt;/p&gt;</description>
      <content:encoded>&lt;img src=&#34;https://davidparry.com/images/tdd-in-the-agentic-era-spec-driven-convert-linkedin.png&#34; alt=&#34;The Specs, Code, Tests triangle kept in sync — an AI agent turns the crank while a human steers, and the test bar goes from red to green&#34; style=&#34;display: block; margin: 0 auto; width: 70%; max-width: 560px;&#34; /&gt;&#xA;&lt;p&gt;&lt;strong&gt;For twenty years I have argued that requirements, tests, and code are the same information at three altitudes, and that the job is to keep them in sync. The argument has not changed — the economics finally have.&lt;/strong&gt; The correct way was always to nail the spec first, but that was the slower way, and when a human sits down at a keyboard there has always been an unspoken rule that whatever they type had better be code. Spec-writing looked like stalling. So teams typed the code, and the spec — if it ever existed — drifted into fiction. That trade-off is now dead. With an agent doing the transformation from requirement to test to implementation, the spec-first path is no longer the slow path. Getting it right is now &lt;em&gt;faster&lt;/em&gt; than winging it, because a well-captured requirement is the thing an agent can actually execute against, and a vague one is the thing you pay for in review, rework, and slop.&lt;/p&gt;&#xA;&lt;p&gt;I recently decided to polish my TDD talk, &lt;em&gt;TDD in the Agentic Era&lt;/em&gt;, with exactly this in mind. The talk always rested on having the spec; the revision makes the spec &lt;strong&gt;mandatory&lt;/strong&gt;. The workflow now begins at the requirement and refuses to move without one — the agent cannot write a test or a line of implementation until it has pulled the spec and turned its acceptance criteria into an executable scenario. In the 60-minute hands-on version, every artifact is real, runnable code — an MCP server, a client agent, a requirements backlog, Gherkin scenarios, and a Red/Green/Refactor state machine that refuses to let anyone, human or AI, refactor on a red bar. It is public in the &lt;a href=&#34;https://github.com/davidparry/tdd-bdd-agentic&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;tdd-bdd-agentic repository&lt;/a&gt;&#xA;, and I use it through this post as the working proof of the argument. But the workshop is the demonstration, not the point. The point is that my old conviction is holding true, and the time to do this — and get it right — is now: why this style finally pays for itself, and why this exact idea, spec-driven development as a triangle, is the reason I joined CodiumAI. I chose them for Itamar&amp;rsquo;s vision — a vision that was not completely deliverable at the time, given where the models were, but was spot on about where this was all going. That company is the one you now know as &lt;a href=&#34;https://www.qodo.ai&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Qodo.&lt;/a&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-we-accomplish-in-the-hour&#34;&gt;What we accomplish in the hour&lt;/h2&gt;&#xA;&lt;p&gt;Everyone has watched an agent demo. Almost nobody has driven the workflow underneath one. That is the gap the workshop closes — and deliberately not by building plumbing. The MCP server and client come completed in the repo (Java, stdio transport, tested to 100% coverage); MCP gets about seven minutes as the standard way to feed your tools into whatever agent you use — Cursor, Claude Desktop, or the bundled CLI client, which narrates the handshake, discovery, and invocation that every IDE does under the hood. The rest of the hour, with 35 of the 60 minutes at your keyboard instead of looking at mine, every attendee:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Drafts and refines a requirement with an agent.&lt;/strong&gt; You describe the intent in a sentence; the agent writes the requirement into the backlog; and then the spec iterates through two server feedback loops. First structure: &lt;code&gt;validate_spec&lt;/code&gt; reports every issue — a criterion missing its Then, a duplicate id, broken JSON — and the agent fixes and re-validates until the spec is valid. Then wording: &lt;code&gt;refine_requirement&lt;/code&gt; critiques the draft — &amp;ldquo;&amp;lsquo;quickly&amp;rsquo; is ambiguous,&amp;rdquo; &amp;ldquo;the story is missing its why,&amp;rdquo; &amp;ldquo;only happy paths, add an edge case&amp;rdquo; — and the LLM rewords against that deterministic feedback until the critique comes back clean. The agent drafts, the server arbitrates, the human approves the final wording.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Drives the full spec-to-green loop.&lt;/strong&gt; A real LLM agent validates the spec, picks up a pending requirement through the seven workflow tools (&lt;code&gt;list_requirements&lt;/code&gt;, &lt;code&gt;get_requirement&lt;/code&gt;, &lt;code&gt;validate_spec&lt;/code&gt;, &lt;code&gt;refine_requirement&lt;/code&gt;, &lt;code&gt;run_tests&lt;/code&gt;, &lt;code&gt;get_tdd_state&lt;/code&gt;, &lt;code&gt;start_refactor&lt;/code&gt;), writes the Gherkin scenario for its acceptance criteria, runs the tests to show RED, implements the simplest code to reach GREEN, and then — only then — refactors.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The kata under all of this is deliberately boring: the String Calculator. The point was never the calculator. The point is the workflow wrapped around it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;three-altitudes-one-discipline&#34;&gt;Three altitudes, one discipline&lt;/h2&gt;&#xA;&lt;p&gt;The center of the talk is a slide I call the three altitudes. It is not &amp;ldquo;just TDD.&amp;rdquo; It composes the three spec-first methodologies, each pinning the system at a different height, and the agent works across all of them:&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;Methodology&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Pins&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Canonical artifact&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;SDD&lt;/strong&gt; (spec-driven)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;the feature&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;a versioned spec with acceptance criteria — in the repo, &lt;code&gt;requirements/requirements.json&lt;/code&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;&lt;strong&gt;BDD&lt;/strong&gt; (behavior-driven)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;one behavior&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;a Gherkin scenario, executed by Cucumber&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;&lt;strong&gt;TDD&lt;/strong&gt; (test-driven)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;one unit&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;a failing JUnit test&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The flow is spec-down. The agent reads a requirement (SDD), turns its acceptance criteria into a tagged Gherkin scenario (BDD), adds unit tests where useful (TDD), and the &lt;code&gt;run_tests&lt;/code&gt; tool runs Cucumber and JUnit together — one bar, one color. Tests are generated &lt;em&gt;from&lt;/em&gt; the spec, not reverse-engineered from the code afterward. That direction is the entire spec-driven claim.&lt;/p&gt;&#xA;&lt;p&gt;Here is what the agent actually produces during the spec-to-green exercise, for a requirement whose acceptance criteria were already phrased Given/When/Then in the spec:&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-gherkin&#34; data-lang=&#34;gherkin&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;@REQ-003&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#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:#a6e22e&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Scenario:&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt; Two numbers separated by a comma are summed&#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:#66d9ef&#34;&gt;    Given &lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;a string calculator&#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:#a6e22e&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;When &lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;I add &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;1,2&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&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:#a6e22e&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Then &lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;the result is &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;3&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here is the prompt the audience pastes into their agent — notice how much of it is workflow, not code:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Using the tdd-workflow tools: validate the spec first, then find the next&#xA;pending requirement, add a Gherkin scenario for its acceptance criteria to&#xA;the feature file (tag it with the requirement id), reuse or add step&#xA;definitions, run the tests to show RED, then implement the simplest code to&#xA;reach GREEN, then refactor. Ask me before each phase change.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;There are two hard human checkpoints in the loop. The first is a spec review: when the agent writes the scenario, the room stops and reads it aloud — &lt;em&gt;is this the behavior we want?&lt;/em&gt; The second is diff approval after GREEN. The agent turns the crank; the human steers. And the discipline is not a suggestion living in a prompt — it lives in the tool. Ask the server to &lt;code&gt;start_refactor&lt;/code&gt; while the bar is red and it refuses. Guardrails belong in code, not in vibes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;twenty-years-a-convert&#34;&gt;Twenty years a convert&lt;/h2&gt;&#xA;&lt;p&gt;I have been a convert to this style for twenty years. Test-first, behavior-first, and above all &lt;strong&gt;requirements-first&lt;/strong&gt; — the conviction that you cannot build the right thing until someone has written down, in a form that can be checked, what the right thing is. I lived through the era when the spec was a Word document that was obsolete before the first sprint ended. Cucumber and Gherkin were the first tools that let the requirement itself become executable, and I never went back.&lt;/p&gt;&#xA;&lt;p&gt;But I should be honest: being a convert and getting to practice it were two different things. The conviction was mostly private, because in most places I worked the requirement either did not exist at all or arrived from someone outside engineering — a product owner, a stakeholder, a business analyst — who was never equipped to turn what they wanted into a technical requirement or a PRD an engineer could build against. What came across was rarely close. That was not their failing. They knew the business cold; they simply were not trained to interrogate a request the way it needs to be interrogated — to dig for the edge cases and the unstated assumptions, to ask the questions that actually need asking, and to pin the answers down in a form that can be checked. So I spent twenty years believing in requirements-first while working in organizations that rarely produced a requirement worth the name.&lt;/p&gt;&#xA;&lt;p&gt;Which is the whole point, because it tells you where the scarce skill always lived. It was never typing the code. It was extracting what is actually needed — from the ticket, from the stakeholder, from the silence between what they said and what they meant — and pinning it down in a testable form. That is why the agentic era feels like vindication rather than disruption to me. An agent is a phenomenal crank-turner, but it can only turn the crank on requirements someone captured well — and if that skill was scarce when a human wrote the code, it is scarcer and more valuable now that an agent will faithfully execute whatever you hand it, good spec or bad. Which leads me to believe the developer&amp;rsquo;s real emerging role is &lt;em&gt;requirements gatherer for the agent&lt;/em&gt; — but that is another post, and I intend to write it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-triangle-that-made-me-join&#34;&gt;The triangle that made me join&lt;/h2&gt;&#xA;&lt;p&gt;In July 2024 I was deciding whether to join a startup called CodiumAI. Plenty of companies were pitching AI code generation; what convinced me was that the CEO, &lt;a href=&#34;https://www.linkedin.com/in/itamarf&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Itamar Friedman&lt;/a&gt;&#xA;, was saying the same thing I had spent twenty years believing — and he had drawn it as a triangle: &lt;strong&gt;spec, tests, code, kept in sync&lt;/strong&gt;. He lays it out most explicitly in his &lt;a href=&#34;https://www.linkedin.com/posts/itamarf_brownfield-activity-7404141226960060416-lhI6&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Gen 3.5 framing&lt;/a&gt;&#xA;, where agents collaborate around three core artifacts, &lt;strong&gt;Specs → Code → Tests&lt;/strong&gt;, improving the spec, transforming it into code, generating and executing tests, and keeping all three tightly aligned as each evolves. I was not joining a company that bolted testing onto generation as an afterthought; I was joining one whose founding thesis was that the three corners are the same information at different altitudes and the job of AI is to keep them aligned. When I joined, that vision was ahead of what the models could deliver. Reading it now, with the workshop in this post running exactly that loop on my laptop, we are close — if not already there.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-workshop-is-the-triangle-running&#34;&gt;The workshop is the triangle, running&lt;/h2&gt;&#xA;&lt;p&gt;Look back at the workshop with the triangle in mind and the mapping is one-to-one:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Spec corner:&lt;/strong&gt; &lt;code&gt;requirements.json&lt;/code&gt;, versioned, with acceptance criteria — drafted with the agent, held structurally valid by &lt;code&gt;validate_spec&lt;/code&gt; and iterated to clean wording by &lt;code&gt;refine_requirement&lt;/code&gt;, and the source of truth the agent implements from.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tests corner:&lt;/strong&gt; the Gherkin feature file and the JUnit tests, generated from the spec, executed together as one bar.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code corner:&lt;/strong&gt; &lt;code&gt;StringCalculator.java&lt;/code&gt;, written last, as the simplest thing that makes the bar green.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The MCP server is the connective tissue — the standard way for an agent to discover your workflow tools and respect your discipline. The human sits at the two checkpoints where judgment lives: &lt;em&gt;is this the right spec?&lt;/em&gt; and &lt;em&gt;is this the right code?&lt;/em&gt; Everything between those checkpoints is crank-turning, and the crank no longer needs to be turned by hand.&lt;/p&gt;&#xA;&lt;p&gt;Twenty years ago, keeping the three corners in sync was a manual discipline that most teams abandoned under deadline pressure. The spec drifted, the tests decayed, and the code became the only truth — unreadable, unverifiable truth. What the agentic era changes is the cost of the discipline. The alignment work that teams always skipped is exactly the work agents are good at.&lt;/p&gt;&#xA;&lt;p&gt;The talk ends with homework: on &lt;code&gt;trunk&lt;/code&gt; — the exact starting point the class clones — requirements REQ-004 through REQ-006 are still pending, and the requirement the room drafted in Exercise 1 is waiting to be taken to green. Clone the &lt;a href=&#34;https://github.com/davidparry/tdd-bdd-agentic&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;repo&lt;/a&gt;&#xA;, point your agent at the tools, and finish the kata on the plane home. Watch the bar go RED, then GREEN. And if you want the answer key, the &lt;code&gt;complete&lt;/code&gt; branch is the kata fully driven through the loop — every requirement implemented, every scenario tagged and green — with CI telling the story on both branches: trunk deliberately fails a class-completeness check because the work is still ahead of you; complete passes everything. Then ask yourself who really wrote the requirement — because that person, not the agent, decided what got built. That&amp;rsquo;s the next post.&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>
