<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Leading EDJE - Medium]]></title>
        <description><![CDATA[We transform businesses and unlock human potential through technology, crafting bespoke solutions that positively disrupt rather than simply solving problems. - Medium]]></description>
        <link>https://medium.com/leading-edje?source=rss----741ac6338981---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Leading EDJE - Medium</title>
            <link>https://medium.com/leading-edje?source=rss----741ac6338981---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 24 May 2026 01:48:55 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/leading-edje" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Shifting Left with Agentic Development]]></title>
            <link>https://medium.com/leading-edje/shifting-left-with-agentic-development-d26d0c6d980f?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/d26d0c6d980f</guid>
            <category><![CDATA[agentic-workflow]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[claude-code]]></category>
            <dc:creator><![CDATA[Delbert Legg]]></dc:creator>
            <pubDate>Tue, 05 May 2026 20:15:23 GMT</pubDate>
            <atom:updated>2026-05-05T20:15:21.802Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*UdT23zI3Aavn6b68.jpg" /></figure><p>In my <a href="https://blog.LeadingEDJE.com/post/fiveiterationsofsdd.html">previous article</a>, I described five iterations of building the same application with Claude Code and arrived at a principle that became the foundation for everything that followed: <em>every rule should have a mechanical enforcement mechanism. If enforcement relies on the agent “remembering” or “knowing,” it will eventually fail.</em></p><p>That article ended with an enforcement hierarchy — prose rules at the bottom, mechanical tooling at the top — and a claim that moving rules up that hierarchy was the key to consistent output. This article is about what happened when I took that seriously.</p><h3>The Problem with “Just Tell It”</h3><p>When you work with an AI coding agent, there’s a tempting pattern: write a rule in your constitution or project docs, and expect the agent to follow it. “Always use the configuration service instead of annotation-based injection.” “Never add a copyleft-licensed dependency.” “Run translations before committing new resource keys.”</p><p>These rules are clear. They’re well-documented. And they get violated constantly.</p><p>Not because the agent is incapable. It can follow any of these rules perfectly when you remind it. The problem is the same one you’d have with a brilliant new hire who’s read the company wiki once and retained about 70% of it. They’ll get most things right, but the tribal knowledge — the stuff that only surfaces when you’re about to do the wrong thing — slips through. Every time.</p><p>The failure mode isn’t dramatic. You don’t get a crash or an error. You get code that looks right, passes tests, and introduces a subtle violation that won’t surface until someone who knows better reviews it. If you’re using AI agents to scale your development capacity, “someone who knows better” is exactly the bottleneck you were trying to eliminate.</p><h3>What Linters Can and Can’t Do</h3><p>The first line of defense is obvious: configure your linters. ESLint, Checkstyle, Spotless, Prettier — these tools are the low-hanging fruit of mechanical enforcement. If a rule can be expressed as a linter configuration, it should be. No discussion, no exceptions. A rule that exists only as prose when a linter plugin could enforce it is a rule that’s waiting to be broken.</p><p>I spent time auditing project constitutions and development rules, classifying each one: <em>Can a linter enforce this? Partially? Not at all?</em> The results were illuminating. About 40% of rules mapped cleanly to existing linter configurations. Another 20% could be partially covered. The remaining 40% fell into a gap that no off-the-shelf tool could reach.</p><p>These weren’t exotic requirements. They were things like:</p><ul><li>“When using pattern A alongside pattern B, you must also include guard C” — a multi-condition rule that spans different parts of the same file</li><li>“New resource keys must have translations registered for all supported locales before they can be committed” — a cross-file consistency check against a data source</li><li>“Dependencies added to build files must not use copyleft licenses” — a domain-specific policy applied to a specific file type at commit time</li><li>“When tests fail, try a clean build before diagnosing the failure” — a behavioral pattern that should be reinforced, not mandated</li></ul><p>Linters are great at syntax and style. They can’t handle multi-condition patterns. They’re useless at cross-file domain logic and behavioral coaching. I needed something that could sit in that gap.</p><h3>Claude Code Hooks: The Mechanism</h3><p>Claude Code has a hook system that fires scripts at specific points in the agent’s tool-use lifecycle. A hook can intercept any tool call — file reads, shell commands, grep searches — either before the tool executes (PreToolUse) or after (PostToolUse). The hook receives the full context of the tool call as JSON on stdin: what tool is being invoked, what arguments it’s being called with, and for post-hooks, what the tool returned.</p><p>The critical capability: a PreToolUse hook can <em>block</em> the tool call entirely. It prints a JSON response with &quot;decision&quot;: &quot;block&quot; and a reason, and the agent never executes the command. Instead, it receives the violation message as feedback and has to fix the problem before retrying.</p><p>This is the shift-left moment. Instead of catching a violation in code review, or worse, in production, you catch it at the exact instant the agent tries to commit the code. The agent can’t proceed until the violation is resolved. It’s not a suggestion. It’s a wall.</p><h3>The Architecture</h3><p>I built a shared engine — a single Python module that handles the mechanical parts: parsing the staged git diff, filtering files by path patterns, matching regex patterns against added lines, and formatting violation output. Each domain concern gets its own hook script that declares a list of rules and calls the engine.</p><p>A rule is a small data structure:</p><pre>Rule(<br>    id=&quot;example-1&quot;,<br>    file_pattern=r&quot;\.java$&quot;,                   # which files to check<br>    pattern=r&quot;some_pattern_to_catch&quot;,          # what to flag<br>    message=&quot;Brief explanation of the fix&quot;,    # what the agent sees<br>    description=&quot;Why this rule exists&quot;,        # human context<br>    exempt_paths=(&quot;path/to/exceptions/&quot;,),     # known exemptions<br>)</pre><p>The engine only examines lines that were <em>added</em> in the current diff — it doesn’t re-litigate existing code. This is important. Retrofitting rules onto a large codebase is a separate initiative. The hooks enforce <em>going forward</em>.</p><p>Each domain gets its own hook file: one for configuration patterns, one for licensing, one for data conventions, one for service layer rules, and so on. They’re organized by the bounded context they protect, not by the type of check they perform. This maps naturally to how teams think about their code — “these are the rules for the data layer” rather than “these are all the regex checks.”</p><p>The hooks are registered in the Claude Code settings file, wired to fire on specific tool patterns:</p><pre>{<br>  &quot;hooks&quot;: {<br>    &quot;PreToolUse&quot;: [<br>      {<br>        &quot;matcher&quot;: &quot;Bash&quot;,<br>        &quot;hooks&quot;: [<br>          {<br>            &quot;type&quot;: &quot;command&quot;,<br>            &quot;command&quot;: &quot;python3 hooks/pre-commit-service-rules.py&quot;,<br>            &quot;if&quot;: &quot;Bash(*git commit*)&quot;<br>          }<br>        ]<br>      }<br>    ]<br>  }<br>}</pre><p>When the agent runs git commit, every registered pre-commit hook fires. Each one independently parses the staged diff and checks its rules. If any hook finds a violation, the commit is blocked and the agent gets a clear error message with the rule ID, file, line number, and what to fix.</p><h3>Three Tiers of Enforcement</h3><p>Not every rule deserves the same severity. I landed on three tiers that match different enforcement needs:</p><p><strong>Tier 1: Blocking pre-commit hooks.</strong> These are the non-negotiable rules. Copyleft license violations. Missing compatibility guards. Configuration anti-patterns that will cause runtime failures. The commit doesn’t happen until these are resolved. The agent gets a clear violation message, fixes the code, and tries again.</p><p><strong>Tier 2: Non-blocking post-commit warnings.</strong> These fire after a successful commit and inject feedback into the conversation. “You committed source files but no tests — make sure coverage exists.” “This directory has source files but no README.” They don’t stop progress, but they create awareness. The agent sees the warning and can choose to act on it in the same session.</p><p><strong>Tier 3: Behavioral nudges.</strong> These aren’t tied to commits at all. They fire on everyday tool use and suggest better approaches. When the agent uses grep to search for what looks like a Java symbol name, a hook suggests using LSP instead — workspace symbol search, find references, go to definition. When a test run fails without a preceding clean build, a hook reminds the agent that stale artifacts cause misleading failures. These are coaching, not enforcement.</p><p>The three tiers mirror how you’d train a human developer. Some things are hard stops — you can’t merge code with a license violation. Some things are reminders — did you forget to write tests? And some things are teaching moments — there’s a better tool for that.</p><h3>What This Looks Like in Practice</h3><p>Here’s a real scenario. The agent is implementing a feature that touches a shared screen component. It writes the code, runs the tests, everything passes. It stages the files and runs git commit.</p><p>The pre-commit hook fires. It examines the staged diff and finds that the file uses two patterns together — a UI component and a title-setting method — without a required compatibility guard. The commit is blocked:</p><pre>Pattern violation — fix before committing.<br><br>  [rule-1] src/screens/OrderScreen.java:47:<br>    setTitle() used alongside DialogHeaderPart but missing compatibility guard.<br>    Shared screens must wrap setTitle() with version check.</pre><p>The agent reads the violation, understands it, adds the guard, re-stages, and commits successfully. Total time added: about 15 seconds. Without the hook, this would have been caught in code review — maybe — after the PR was already up and a human reviewer happened to know about this particular compatibility requirement.</p><p>Here’s another scenario. The agent adds a new dependency to a build file. The licensing hook fires, identifies it as a GPL-licensed package, and blocks the commit with a suggestion for an approved alternative. The agent swaps the dependency and proceeds. No human had to remember that mysql-connector-java is GPL but mysql-connector-j is EPL.</p><p>And a behavioral example: the agent runs tests, they fail, and the clean-build nudge fires. Instead of spending ten minutes diagnosing a phantom failure caused by stale compiled artifacts, the agent runs a clean build first. This one isn’t blocking — it’s coaching. But it saves real time by short-circuiting a common debugging dead end.</p><h3>Beyond Regex: Cross-File Consistency</h3><p>Some of the most valuable hooks don’t use the shared regex engine at all. They implement custom logic for checks that span multiple files or require reading project state.</p><p>One hook checks that when you add new internationalization keys to a properties file, those keys have corresponding translations registered in a central data file for every supported locale. It reads the locale configuration, builds a set of what’s already translated, diffs the staged properties file for new keys, and blocks the commit if any key is missing translations. This prevents a class of runtime bug — a user switching to an unsupported locale and seeing raw key names — that no linter could catch.</p><p>This is the kind of rule that would traditionally live in a PR checklist: “Did you add translations for all locales?” Checklists are prose. Prose gets skipped. A mechanical check doesn’t.</p><h3>The Economics of Enforcement</h3><p>There’s a cost argument here that goes beyond just “catching bugs earlier.”</p><p>In the previous article, I tracked the token cost of each iteration. The expensive part wasn’t the initial code generation — it was the rework cycle. Agent writes code, reviewer catches violation, agent fixes, reviewer re-reviews. Each round trip costs tokens, time, and context window space.</p><p>Pre-commit hooks collapse that cycle to zero round trips for the classes of violations they cover. The agent never produces a PR with a licensing violation or a missing compatibility guard. The human reviewer never has to flag it. The fix happens at the moment of creation, not after the fact.</p><p>More importantly, these hooks scale. Adding a new rule is a ten-minute task: write the regex, add a test case, register the hook. That rule then applies to every future commit, in every branch, for every agent session. The cost of enforcement is paid once. The cost of <em>not</em> enforcing is paid on every violation that slips through.</p><p>Building this system reinforced the central thesis from the first article, but added nuance.</p><p><strong>The enforcement hierarchy is real, but it has more levels than I initially described.</strong> I originally thought of it as a binary: prose rules versus mechanical enforcement. In practice, there’s a spectrum. Blocking hooks are at the top. Non-blocking warnings are in the middle. Behavioral nudges are below that. Prose rules are at the bottom. Each level up buys you more consistency, but also more rigidity. Not everything belongs at the top.</p><p><strong>Rules should encode “why,” not just “what.”</strong> Every rule in the system carries a description field explaining the reasoning. When the agent hits a violation, it doesn’t just see “don’t do X” — it sees <em>why</em> X is prohibited. This context helps the agent make better fixes and, occasionally, helps it recognize when a rule doesn’t apply to its specific situation. A rule without a reason is a rule that gets cargo-culted.</p><p><strong>Exemptions are first-class citizens.</strong> Almost every rule has known exceptions — paths where the pattern is intentionally used, file types where it doesn’t apply, modules that predate the convention. The rule system supports exempt paths and exempt suffixes explicitly. This prevents the hooks from becoming a source of false positives that train the agent (and the developer) to ignore them.</p><p><strong>Post-hooks are underrated.</strong> I initially focused almost entirely on blocking hooks. But the behavioral nudges — “use LSP instead of grep for symbol search,” “try a clean build before debugging test failures” — turned out to be surprisingly high-value. They don’t prevent mistakes; they prevent wasted time. Over dozens of sessions, the accumulated time savings from the agent not chasing phantom failures or doing inefficient searches add up significantly.</p><p><strong>The diff-only principle matters.</strong> Only checking added lines, not the entire file, is critical for adoption. You can introduce these hooks to a legacy codebase without triggering thousands of violations on existing code. The hooks enforce the new standard going forward, which means you can adopt them incrementally without a massive remediation effort.</p><h3>Where This Is Heading</h3><p>The system I’ve described is essentially a pattern-matching layer between the AI agent and the git repository. It’s regex-based, diff-scoped, and organized by domain. It catches a meaningful class of violations that linters can’t reach, and it does so at the earliest possible moment.</p><p>But there’s an obvious next frontier: hooks that don’t just match patterns but understand semantics. A hook that can verify an API contract is honored across service boundaries. A hook that checks whether a database migration is backwards-compatible with the previous schema. A hook that validates that a new endpoint follows the project’s authentication pattern, not just syntactically but structurally.</p><p>Some of this is possible today with more sophisticated hook implementations — AST parsing instead of regex, for example. Some of it will require hooks that can call out to other tools or services. The architecture supports it; the hook system doesn’t care what your script does internally, only what decision it returns.</p><p>The broader point is this: as AI agents take on more of the implementation work, the value shifts from writing code to defining and enforcing the constraints that code must satisfy. The developers who do this well won’t be the ones who write the most detailed constitutions. They’ll be the ones who build the most effective mechanical enforcement around their constraints. Rules are wishes. Hooks are walls.</p><p><em>Originally published at </em><a href="https://blog.leadingedje.com/post/claudeagenthooks.html"><em>https://blog.leadingedje.com</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d26d0c6d980f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/leading-edje/shifting-left-with-agentic-development-d26d0c6d980f">Shifting Left with Agentic Development</a> was originally published in <a href="https://medium.com/leading-edje">Leading EDJE</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agentic AI Tools & Resources for Senior Developers]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/agentic-ai-tools-resources-for-senior-developers-88dbaa425a68?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/1200/1*59qa5xEOvNpcfp2NXWCV1g.png" width="1200"></a></p><p class="medium-feed-snippet">A curated guide to tools, concepts, and best practices for AI-assisted coding</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/agentic-ai-tools-resources-for-senior-developers-88dbaa425a68?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/agentic-ai-tools-resources-for-senior-developers-88dbaa425a68?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/88dbaa425a68</guid>
            <category><![CDATA[github-copilot]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[cursor]]></category>
            <category><![CDATA[claude]]></category>
            <dc:creator><![CDATA[Matt Eland]]></dc:creator>
            <pubDate>Tue, 31 Mar 2026 19:39:42 GMT</pubDate>
            <atom:updated>2026-03-31T19:39:41.451Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Teaching an AI to Build Software]]></title>
            <link>https://medium.com/leading-edje/teaching-an-ai-to-build-software-bc5fe6314e60?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/bc5fe6314e60</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[spec-kit]]></category>
            <category><![CDATA[claude]]></category>
            <dc:creator><![CDATA[Delbert Legg]]></dc:creator>
            <pubDate>Wed, 18 Mar 2026 13:34:31 GMT</pubDate>
            <atom:updated>2026-03-18T13:34:30.105Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BJyGIS0Bhbt0Z0gxHyFfKA.jpeg" /></figure><h3>Five Iterations of Spec-Driven Development with Claude Code</h3><p>I spent a week building the same full-stack application five times with Claude Code. Not because the first attempt failed — but because I wanted to understand how to get consistently good output from an AI coding agent.</p><p>The application is an interview scheduling tool: interviewers set availability, the system pairs them based on roles, candidates pick time slots, and everyone gets notified. It’s a real-world CRUD app with enough complexity to expose process failures — 5 user stories, 17 functional requirements, 8 database entities, 10 pages of UI, role-based auth, and transactional booking with double-booking prevention.</p><p>Each iteration used the same spec and the same model (Claude Opus 4.6 via Claude Code). What changed was the process I wrapped around it. Here’s what I learned.</p><h3>The Setup</h3><p>I’m using <a href="https://github.com/github/spec-kit">spec-kit</a>, an open-source spec-driven development tool that can be used with Claude Code. It provides a set of slash commands that enforce a structured workflow: /speckit.spec to write the specification, /speckit.plan to design the architecture, /speckit.tasks to generate an implementation plan, and /speckit.implement to execute it. These commands are backed by templates, rules, and a project constitution that define how the agent should work.</p><p>I used spec-kit from the very first iteration. The early runs used the default templates. Starting with iteration 3, I began customizing them — adding enforcement tables, reconciliation checks, and lessons-learned integration. The tool gives you the skeleton; I spent the week figuring out what to put in the bones.</p><p>The constitution is a markdown file that establishes non-negotiable principles: infrastructure before features, TDD, API-first design, security-first, and framework-native patterns. Think of it as a style guide for the agent’s decision-making process, not just its code output.</p><h3>Iteration 1: Incremental TDD ($634 API-equivalent)</h3><p>All five iterations used spec-kit from the start — the spec, plan, and task breakdown were generated through its workflow every time. What changed across iterations wasn’t whether I used the tool, but how much I customized its templates and enforcement mechanisms.</p><p>The first attempt followed every rule in the book. Write a failing test, implement the minimum to pass it, refactor, repeat. Three sessions over two days: one for spec and planning, two for implementation.</p><p>The result was excellent. 155 tests across 30 files — unit, integration, and E2E. Every route worked. Rich seed data. Proper migrations. The code was production-grade.</p><p>But the process was brutally expensive. The TDD cycle (write test, run, fail, implement, run, pass) generates many small API round-trips, and each call re-reads the growing conversation history. At 2,880 API calls and 238 million cache-read tokens, this approach burned through capacity fast. The same codebase could have been produced with far fewer round-trips.</p><p><strong>Takeaway</strong>: Strict TDD with an AI agent multiplies API calls without proportional quality gains. The agent doesn’t need the pedagogical discipline of TDD — it doesn’t learn by failing. What it needs is a clear spec and test expectations upfront.</p><h3>Iteration 2: Big Bang ($153 API-equivalent)</h3><p>The opposite extreme: throw the entire spec at the agent in a single session and let it write everything at once. One session, 524 API calls, a quarter of the cost.</p><p>The code looked right. Unit tests passed. Terraform infrastructure and deploy pipelines included — things the incremental approach never got to. On paper, feature parity.</p><p>Then I tried to run it.</p><p>The schedule routes returned 500 errors. No seed data beyond round config, so every page was empty. No migrations directory — you had to push the schema with drizzle-kit push. No logout endpoint. No ESLint rule enforcing the service layer boundary. Unit tests passed because they mocked the database, so the relational query bugs never surfaced.</p><p><strong>The code compiled. The tests passed. The application didn’t work.</strong></p><p><strong>Takeaway</strong>: An AI agent will satisfy the literal requirements you give it. If you don’t require runtime validation, you don’t get runtime validation. Unit tests with mocked dependencies prove the code is internally consistent, not that it functions.</p><h3>Iteration 3: Customizing the Templates ($177 API-equivalent)</h3><p>The first two iterations used spec-kit’s default templates as-is. For iteration 3, I started customizing them — refining the plan template’s structure, adding more specificity to the task generation, and tightening the constitution’s project-specific constraints.</p><p>The output was meaningfully better: proper migrations, rich seed data, ESLint service boundary enforcement, and a codebase that worked out of the box. The overhead of the template customization was minimal — $23 more than the raw big-bang approach.</p><p>Still no E2E tests, though. The spec said to write them, the constitution mandated them, the CLAUDE.md file listed the command to run them. But no E2E test was generated.</p><p><strong>Takeaway</strong>: Structured planning works. But rules that exist only in documentation get ignored. The agent doesn’t “forget” them — it never reads them at the decision point where they matter.</p><h3>The Lessons-Learned Breakthrough</h3><p>After iteration 3, I sat down and documented every failure across all three implementations. Not as retrospective notes — as enforcement mechanisms.</p><p>Each lesson followed a template:</p><ul><li><strong>What was observed</strong>: The specific failure</li><li><strong>What rule existed</strong>: Where the requirement was documented</li><li><strong>Why it wasn’t enforced</strong>: The structural reason the rule was bypassed</li><li><strong>What enforcement was added</strong>: The mechanical change to prevent recurrence</li></ul><p>Five failures produced five enforcement entries:</p><p><strong>LL-001: Spec technology silently ignored.</strong> shadcn-svelte was listed as an active technology, but all 9 pages were built with raw Tailwind. The spec mentioned it once in the header — never in any task. Fix: Added a <strong>Tech Stack Manifest</strong> table to the plan template mapping each technology to its setup phase and a <strong>Tech Stack Reconciliation</strong> table to the tasks template requiring a binary check that every manifest entry has a corresponding setup task.</p><p><strong>LL-002 &amp; LL-003: Existing linter rules ignored.</strong> ESLint was configured to block direct database imports in routes and to ban inline comments. The agent didn’t read eslint.config.js before writing code. The violations were caught at lint time, but the code was already written — wasted effort. No template change needed — the enforcement was already mechanical. The lesson was that the agent should read lint config before writing new code.</p><p><strong>LL-004: Infrastructure skipped.</strong> The task plan had 76 feature tasks and zero infrastructure tasks, despite the constitution saying infrastructure must come first. The rule was in a separate file from where task generation decisions were made. Fix: Marked the infrastructure phase as <strong>BLOCKING</strong> in the tasks template with explicit verification checkpoints.</p><p><strong>LL-005: E2E tests completely omitted.</strong> Both the incremental and big-bang approaches shipped zero E2E tests. The requirement existed in three separate rule files but was never surfaced during planning or task generation. Fix: Added a <strong>Critical User Flows</strong> table to the plan template and an <strong>E2E Test Coverage Reconciliation</strong> table to the tasks template — the same pattern as the tech stack manifest.</p><p>The pattern across all five failures was identical: <strong>rules that exist only as prose get silently dropped.</strong> The agent doesn’t “refuse” to follow them. It never encounters them at the moment it makes the decision to skip them.</p><p>This produced a principle I now consider the most important insight from this entire experiment:</p><blockquote><em>Every rule should have a mechanical enforcement mechanism. If enforcement relies on the agent “remembering” or “knowing,” it will eventually fail. Preference order: </em><strong><em>Tooling &gt; Template tables &gt; Rule files &gt; Documentation.</em></strong></blockquote><h3>Iteration 4: Quality Gates ($334 API-equivalent)</h3><p>Same spec, same model, but now the speckit commands load all lessons-learned files before generating plans and tasks. The enforcement tables are baked into the templates.</p><p>Cost nearly doubled from iteration 3. That’s the “quality tax” — the work that was silently skipped before (shadcn components, E2E tests, tech stack verification) was now actually being done. The $158 difference between v3 and v4 is the cost of the code actually working.</p><p>shadcn components were installed via CLI instead of hand-written. E2E tests existed. The tech stack manifest was reconciled. All routes worked.</p><p>But when I reviewed the E2E tests, many were shallow. They navigated to a page, checked that a heading existed, verified some seed data was rendered, and called it done. They tested that the server rendered HTML, not that the application functioned.</p><p><strong>Takeaway</strong>: Getting the agent to produce E2E tests and getting it to produce <em>good</em> E2E tests are different problems. The reconciliation table ensured tests existed. Nothing ensured they exercised real user interactions.</p><h3>Iteration 5: Real E2E Tests ($344 API-equivalent)</h3><p>I asked a simple question: “Do the E2E tests actually test functionality of the requirements?”</p><p>They didn’t. So I rewrote them. All 26 tests now exercise real user flows: creating entities through forms, editing and verifying persistence, advancing interview rounds, booking interview slots, checking that notifications appear.</p><p>This immediately surfaced bugs that no static test would catch:</p><p><strong>Svelte 5 checkbox binding bug.</strong> Checkboxes using checked={formData.roles.includes(role)} with $derived lost their state during form submission. SvelteKit’s client-side form handler re-collected FormData after Svelte’s reactivity reset the checkbox state during the click-to-submit event propagation. The fix was bind:group with $state. This was a framework-level interaction bug that only appears when you actually submit a form with checkboxes.</p><p><strong>SvelteKit named action conflict.</strong> Pages with both a default action and named actions (toggleActive, advanceRound) threw a 500 error in production. SvelteKit validates this at runtime with check_named_default_separate. The fix was renaming default to update.</p><p>Both bugs passed unit tests, passed type checking, and passed the shallow E2E tests from v4. They only appeared when a real user interaction — checking a box and submitting a form — was tested end-to-end.</p><p>The cost difference between v4 and v5 was $10. Nearly identical cost, significantly higher quality.</p><p><strong>Takeaway</strong>: The most valuable tests are the ones that replicate what a user actually does. A $10 investment in rewriting superficial tests found bugs that would have shipped to production.</p><h3>What I Learned</h3><h3>The Five Iterations at a Glance</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/933/1*YDQOBpepG111u5EdmVqImw.png" /></figure><h3>The Enforcement Hierarchy</h3><p>The single most impactful discovery: rules at different positions in the enforcement hierarchy have dramatically different compliance rates.</p><p>If you care about a rule, move it up the hierarchy. Don’t write “always use shadcn components” in a README. Add a Tech Stack Manifest table that forces the agent to list each technology, its setup command, and a verification artifact. Then add a reconciliation table that must show a checkmark for each entry.</p><h3>Lessons Learned Must Be Active, Not Archival</h3><p>Writing down what went wrong is useless if the system doesn’t read it next time. The speckit commands now scan all specs/*/lessons-learned.md files before generating plans, tasks, or implementations. Each entry includes the enforcement mechanism and verification step. The agent must confirm compliance in its output.</p><p>This is the difference between a retrospective and a guardrail. A retrospective says “we should do X next time.” A guardrail makes X a required input to the next run.</p><h3>The Cost of Quality Is Predictable</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/933/1*udOBBL-roGG3rI_o_R1dNA.png" /></figure><p>The cheap approach ($153) produced code that crashed. The expensive approach ($634) produced great code but burned 4x the capacity for diminishing returns. The sweet spot (~$340) produced working, tested, well-structured code with proper component libraries and E2E coverage.</p><h3>What the Agent Does Well vs. What It Needs Help With</h3><p><strong>Does well autonomously:</strong> — Writing CRUD services, database schemas, migrations — Implementing business logic from clear specs — Setting up CI pipelines, Docker configs, project scaffolding — Writing unit tests (when told to) — Following established patterns consistently once shown one example</p><p><strong>Needs structural enforcement:</strong> — Using CLI tools instead of hand-writing library code — Writing E2E tests (and making them test real interactions) — Reading existing config (lint, tsconfig) before writing new code — Prioritizing infrastructure over features — Maintaining consistency across many files (tech stack drift)</p><p>The failure mode is never “the agent can’t do it.” It’s “the agent takes the path of least resistance.” (or, the agent does whatever is most common to have been done by people on stack overflow or other documentation, etc) Hand-writing a component is easier than running a CLI. Mocking the database is easier than testing with real data. Writing &lt;div class=&quot;rounded border p-2&quot;&gt; is easier than importing a shadcn component.</p><p>Your job as the human in the loop isn’t to write code. It’s to build the system of constraints that makes the path of least resistance also the path of highest quality.</p><h3>Sub-Agents and Skills: Making the Process Self-Aware</h3><p>One of the more interesting layers I built was a set of Claude Code sub-agents and skills that automate the meta-work — analyzing projects, initializing templates, enforcing rules, and guarding against tech-stack bias.</p><h3>Project Analyzer Agent</h3><p>The first problem I hit when trying to make spec-kit work across different projects was that the templates contained generic placeholders like lint &amp;&amp; type-check &amp;&amp; test for CI pipeline commands. Every project uses different commands — pnpm lint vs cargo clippy vs ruff check. Filling these in manually defeated the purpose.</p><p>So I built a project-analyzer sub-agent. It’s a Claude Code agent (running on Sonnet for cost efficiency) that scans the project root, detects the tech stack from config files (package.json, Cargo.toml, go.mod, pyproject.toml), identifies the package manager from lockfiles, extracts all available commands from scripts/targets, and maps them to canonical categories: lint, type_check, test_unit, test_e2e, build, db_migrate, etc.</p><p>It outputs a structured JSON manifest that gets saved to .specify/memory/commands.json. The /speckit-init skill then uses this manifest to customize spec-kit’s templates for the specific project — replacing generic “run lint” with the actual pnpm lint command, pre-filling the language and framework in the plan template, and populating the CLAUDE.md commands table.</p><p>The result: when you run /speckit-init on a SvelteKit project, the templates know about pnpm check, pnpm test:e2e, and Vitest. Run it on a Rust project and they know about cargo clippy, cargo test, and cargo fmt --check. The agent figures this out by reading the project’s actual config — no manual setup needed.</p><h3>Rule Enforcer Agent</h3><p>After documenting eight lessons learned about rules being ignored, I realized some of those rules could be mechanically enforced by tools that already existed in the project — if someone configured them correctly.</p><p>The rule-enforcer agent reads all the development rules (constitution, coding standards, design principles), classifies each one as automatable, partially automatable, or judgment-only, and maps the automatable ones to specific linter rules, pre-commit hooks, or config checks. It verifies that the proposed rules actually exist (no hallucinated ESLint rule names), detects conflicts with existing config, and counts how many current violations each rule would surface.</p><p>For example, it reads “No any — use unknown + type guards” from the coding standards and maps it to @typescript-eslint/no-explicit-any: error (this is good; the trend of abandoning free, deterministic, fast tools for ai hopes-and-dreams is not my favorite lol), confirms the plugin is installed, and reports 0 existing violations. It reads “No hardcoded secrets” and proposes a gitleaks pre-commit hook. It reads “SRP: A module changes for one reason only” and correctly classifies it as judgment-only — no tool can enforce that.</p><p>The output is a manifest of enforceable vs. judgment-only rules. The enforceable ones get baked into linter config and pre-commit hooks. The judgment-only ones stay as context for the AI agent. This directly implements the enforcement hierarchy: move rules up from “documentation the agent might read” to “tooling that blocks the commit.”</p><h3>Template Guardian Agent</h3><p>Lesson LL-007 taught me that project-specific tech references leak into global templates. When I added anti-approximation rules to prevent the agent from hand-writing shadcn components, the examples I used referenced shadcn, bits-ui, pnpm, and Drizzle — all from this specific project. Those templates are shared across every project, regardless of language.</p><p>The template-guardian agent audits global templates for tech-stack specificity. It has a list of banned terms (shadcn, bits-ui, drizzle, prisma, zustand, nextjs, sveltekit) that must have zero matches unless they’re part of a multi-ecosystem example list. Every example in a template must include references from at least two different ecosystems. It runs after any template edit and flags violations before they propagate.</p><p>This is the kind of problem that only surfaces when you use the same tooling across multiple projects. A template that says “run npx shadcn init” works fine for a SvelteKit project but actively misleads the agent when it’s generating a Rust plan.</p><h3>How They Fit Together</h3><p>These agents form a pipeline:</p><ol><li><strong>/speckit-init</strong> runs the project analyzer, produces a command manifest, and customizes templates for the project’s tech stack</li><li><strong>/enforce-rules</strong> runs the rule enforcer, maps development rules to linter config and pre-commit hooks</li><li><strong>Template guardian</strong> runs after any template edit to prevent tech-stack bias from leaking into shared templates</li><li>The customized templates and enforced rules feed into the spec-kit workflow (/speckit.spec -&gt; /speckit.plan -&gt; /speckit.tasks -&gt; /speckit.implement)</li></ol><p>Each agent runs on Sonnet with restricted tool access — the project analyzer and rule enforcer are read-only with bash access for command verification. The template guardian can write files since its job is to fix templates. They’re defined as markdown files in the Claude Code profiles directory, which means they persist across sessions and projects.</p><h3>The System Today</h3><p>After five iterations, here’s what the process looks like. Spec-kit provides the workflow framework (spec, plan, tasks, implement commands). The customizations I built on top are what make it enforce quality:</p><ol><li><strong>Hardened tooling</strong> (highest enforcement): ESLint rules, type checking, pre-commit hooks, CI pipelines. The /enforce-rules skill reads every rule from the constitution and coding standards, identifies which ones can be mechanically enforced, and writes the actual linter config and git hooks. A rule like “no direct database imports in routes” doesn’t stay as prose — it becomes an ESLint no-restricted-imports entry that fails the build. This is the strongest layer because the code literally won’t merge if it violates these rules.</li><li><strong>Sub-agents</strong> (automated analysis): The project analyzer, rule enforcer, and template guardian run as Sonnet-powered sub-agents that handle meta-work — detecting the tech stack, mapping rules to tooling, and preventing template drift. They make the system self-bootstrapping: point it at a new project and it figures out the commands, framework, and enforceable rules automatically.</li><li><strong>Template tables</strong> (my customization on spec-kit): Tech Stack Manifests, E2E Coverage Reconciliation tables, Critical User Flows tables. These are the mechanical enforcement layer I added to spec-kit’s default templates — binary checks that force the agent to account for each requirement.</li><li><strong>Lessons learned</strong> (my customization on spec-kit): Not retrospectives. Active enforcement entries with specific mechanisms and verification steps. I modified spec-kit’s plan, tasks, and implement commands to scan these files and apply them as constraints during every run.</li><li><strong>Constitution and rules</strong> (spec-kit feature): Project-level principles that narrow global coding standards to the specific domain and tech stack. These feed into both the rule enforcer (which hardens them into tooling) and the spec-kit workflow (which loads them as context).</li><li><strong>Spec-kit commands</strong> (open source): The workflow backbone — spec, plan, tasks, implement. I customized them to load lessons-learned files, validate against templates, and produce structured artifacts with compliance checks.</li></ol><p>The total investment to build this system was about a week. Spec-kit gave me a solid foundation; the iteration-by-iteration customization — especially the sub-agents and the enforcement pipeline that converts prose rules into linter configs — is what turned it into a reliable quality system. The payoff is that I can now hand the agent a new feature spec and be reasonably confident that the output will work, be tested, use the correct technology, and follow the project’s conventions — because compliance is structural, not aspirational.</p><h3>A Note on Costs</h3><p>Every cost figure in this post is an <strong>API-equivalent estimate</strong>, not an actual bill. All five iterations ran on a Claude Max subscription — a flat monthly fee with usage limits. I didn’t pay per-token for any of this.</p><p>So why quote dollar amounts at all? Because they’re useful for comparing relative efficiency between approaches. The question isn’t “how much did this cost?” but “how much compute did each approach consume?” API pricing gives a consistent unit of measurement.</p><h3>How the Numbers Were Calculated</h3><p>Claude Code tracks token usage per session in its transcript files. Each session records four token categories per API call:</p><ul><li><strong>Input tokens</strong>: The actual new content sent (user messages, tool results)</li><li><strong>Cache write tokens</strong>: First time the model sees context in a conversation — written to cache at 1.25x input price</li><li><strong>Cache read tokens</strong>: Subsequent reads of already-cached conversation history — charged at 0.1x input price</li><li><strong>Output tokens</strong>: The model’s response (code, explanations, tool calls)</li></ul><p>I pulled these numbers from the session transcripts and applied Anthropic’s published Opus 4.6 API rates:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/933/1*OUSpLCzpPHxBXpP8Ei9lOQ.png" /></figure><p>For each session, the formula is:</p><pre>cost = (input × $15 + cache_write × $18.75 + cache_read × $1.50 + output × $75) / 1,000,000</pre><p>The dominant cost driver across all approaches is <strong>cache reads</strong>. Every API call re-reads the growing conversation history. A 2,000-call session doesn’t just generate 2,000 responses — it re-reads the entire conversation 2,000 times. This is why the incremental TDD approach ($634) cost 4x the big-bang approach ($153): not because it produced more code, but because 2,880 small round-trips each re-read an ever-growing context window.</p><h3>What This Means for Max Subscribers</h3><p>If you’re on Claude Max, these dollar figures don’t map to your bill — you’re paying a flat rate. But they do map to your <strong>usage limits</strong>. Higher token consumption burns through your conversation allowance faster. The relative efficiency still matters: a $340-equivalent session uses roughly 2.2x the capacity of a $153-equivalent session, regardless of how you’re billed.</p><h3>The Raw Numbers</h3><p>For full token breakdowns by session, see specs/001-interview-scheduling/implementation-comparison.md in the repository.</p><h3>Should You Do This?</h3><p>If you’re using AI coding agents for one-off scripts or small features, probably not. The overhead of a constitution, templates, and enforcement tables isn’t worth it for a 50-line function.</p><p>If you’re building real applications with an AI agent as a primary contributor — especially if you’re running the same kind of feature implementation repeatedly — this process pays for itself quickly. The first iteration costs time to set up. Every subsequent iteration benefits from the accumulated guardrails.</p><p>The key insight isn’t about any specific tool or template. It’s this: <strong>treat AI coding agents like junior developers who are brilliant but easily distracted.</strong> They’ll write great code if you set up the right structure. They’ll cut corners if you rely on them to remember rules from a document they read 10,000 tokens ago.</p><p>Build the guardrails. Make them mechanical. And when something goes wrong, don’t just fix it — add enforcement so it can’t happen again.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bc5fe6314e60" width="1" height="1" alt=""><hr><p><a href="https://medium.com/leading-edje/teaching-an-ai-to-build-software-bc5fe6314e60">Teaching an AI to Build Software</a> was originally published in <a href="https://medium.com/leading-edje">Leading EDJE</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Self-Improving AI Application Architectures]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/self-improving-ai-application-architectures-9e53bf887ee6?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/900/0*-PHwle_sbTVJ7fXo.png" width="900"></a></p><p class="medium-feed-snippet">Building a self-improving AI dungeon master</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/self-improving-ai-application-architectures-9e53bf887ee6?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/self-improving-ai-application-architectures-9e53bf887ee6?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/9e53bf887ee6</guid>
            <category><![CDATA[quality-assurance]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[prompt-engineering]]></category>
            <category><![CDATA[ai-agent]]></category>
            <dc:creator><![CDATA[Matt Eland]]></dc:creator>
            <pubDate>Tue, 10 Mar 2026 19:31:57 GMT</pubDate>
            <atom:updated>2026-03-10T19:31:59.034Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[16 Tips for Writing AI-Ready C# Code]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/16-tips-for-writing-ai-ready-c-code-0767f99edccf?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/1170/0*37ZcAk74dFT2l59p.png" width="1170"></a></p><p class="medium-feed-snippet">How to make it easy for AI agents to work with your&#xA0;.NET codebase</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/16-tips-for-writing-ai-ready-c-code-0767f99edccf?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/16-tips-for-writing-ai-ready-c-code-0767f99edccf?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/0767f99edccf</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[generative-ai-tools]]></category>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[csharp]]></category>
            <category><![CDATA[cursor]]></category>
            <dc:creator><![CDATA[Matt Eland]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 17:43:39 GMT</pubDate>
            <atom:updated>2025-12-15T17:43:38.134Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Six Months Changes .NET STS]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/six-months-changes-dotnet-sts-26aee594078c?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/1280/1*gFJFt4WqjucEeUx9ywM4pQ.jpeg" width="1280"></a></p><p class="medium-feed-snippet">How 24-month support changes your upgrade strategy</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/six-months-changes-dotnet-sts-26aee594078c?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/six-months-changes-dotnet-sts-26aee594078c?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/26aee594078c</guid>
            <category><![CDATA[dotnet]]></category>
            <dc:creator><![CDATA[Victor Frye]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 17:43:30 GMT</pubDate>
            <atom:updated>2025-12-15T17:43:29.074Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Cozy Aspire Dashboarding]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/cozy-aspire-dashboarding-9cf510a7a57c?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/1280/1*0UiYY_H3DjLmATkSk_-H_w.jpeg" width="1280"></a></p><p class="medium-feed-snippet">Make the Aspire dashboard your home with custom URLs and icons</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/cozy-aspire-dashboarding-9cf510a7a57c?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/cozy-aspire-dashboarding-9cf510a7a57c?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/9cf510a7a57c</guid>
            <category><![CDATA[cloud-native]]></category>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[aspire]]></category>
            <dc:creator><![CDATA[Victor Frye]]></dc:creator>
            <pubDate>Tue, 28 Oct 2025 19:51:05 GMT</pubDate>
            <atom:updated>2025-10-29T13:53:31.526Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Local Friendly .NET Aspire]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/local-friendly-aspire-modeling-c88d12dc63da?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/1280/1*we7JA4ex309JkhSXmsUGnQ.jpeg" width="1280"></a></p><p class="medium-feed-snippet">Modeling your local environment</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/local-friendly-aspire-modeling-c88d12dc63da?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/local-friendly-aspire-modeling-c88d12dc63da?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/c88d12dc63da</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[cloud-native]]></category>
            <category><![CDATA[aspire]]></category>
            <dc:creator><![CDATA[Victor Frye]]></dc:creator>
            <pubDate>Tue, 28 Oct 2025 19:50:52 GMT</pubDate>
            <atom:updated>2025-10-28T19:50:51.535Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[From Dev to DevOps]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/from-dev-to-devops-9a1e36b7cb28?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/1280/1*HhC2dBuc3qhuviPXXGZZxQ.jpeg" width="1280"></a></p><p class="medium-feed-snippet">A learning path for developers interested in DevOps</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/from-dev-to-devops-9a1e36b7cb28?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/from-dev-to-devops-9a1e36b7cb28?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/9a1e36b7cb28</guid>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Victor Frye]]></dc:creator>
            <pubDate>Wed, 22 Oct 2025 19:06:46 GMT</pubDate>
            <atom:updated>2025-10-22T19:06:42.758Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Our Favorite New Features in .NET 10 and C# 14]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/leading-edje/our-favorite-new-features-in-net-10-and-c-14-506c13ad99e5?source=rss----741ac6338981---4"><img src="https://cdn-images-1.medium.com/max/1600/0*9oHdm4WkQMaTzpEM.png" width="1600"></a></p><p class="medium-feed-snippet">See our favorite new C#, ASP.NET, and EF Core features in&#xA0;.NET&#x2019;s latest LTS release</p><p class="medium-feed-link"><a href="https://medium.com/leading-edje/our-favorite-new-features-in-net-10-and-c-14-506c13ad99e5?source=rss----741ac6338981---4">Continue reading on Leading EDJE »</a></p></div>]]></description>
            <link>https://medium.com/leading-edje/our-favorite-new-features-in-net-10-and-c-14-506c13ad99e5?source=rss----741ac6338981---4</link>
            <guid isPermaLink="false">https://medium.com/p/506c13ad99e5</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[csharp]]></category>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[aspnetcore]]></category>
            <category><![CDATA[entity-framework-core]]></category>
            <dc:creator><![CDATA[Matt Eland]]></dc:creator>
            <pubDate>Wed, 22 Oct 2025 19:06:17 GMT</pubDate>
            <atom:updated>2025-10-22T19:05:54.599Z</atom:updated>
        </item>
    </channel>
</rss>