<?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:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Tech Blog (mainly!)]]></title><description><![CDATA[Thoughts, stories and ideas.]]></description><link>https://blog.shanelee.name/</link><generator>Ghost 0.8</generator><lastBuildDate>Thu, 28 May 2026 15:42:59 GMT</lastBuildDate><atom:link href="https://blog.shanelee.name/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Agentic Reflexion: Teach Your AI to Doubt Its Own Plan]]></title><description><![CDATA[<p><strong>Your AI wrote a great plan. It's probably wrong. Here's how to catch that before you build.</strong></p>

<pre><code>        ┌──────────────────────────────────────────────┐
        │                                              │
   You  │                                         refine
    │   │                                              │
    ▼   ▼                                              │
┌─────────────────────┐                               │
│      Plan Mode      │  ← Model A (e.g., Sonnet)     │
│  (Read · Reason ·   │                               │
│    Propose)         │                               │
└─────────────────────┘                               │
           │                                          │
           │  Draft Plan                              │
           ▼                                          │
┌─────────────────────┐                               │
│    Plan Reviewer    │  ← Model B (e.g., Opus)       │
│  (Critique · Flag · │    Fresh context              │
│    Rank by Risk)    │    Adversarial prompt         │
└─────────────────────┘                               │
           │                                          │
    ┌──────┴──────┐                                   │
    │  Blockers?  │ ──── yes ─────────────────────────┘
    └──────┬──────┘
           │ no</code></pre>]]></description><link>https://blog.shanelee.name/2026/05/12/agentic-reflexion-teach-your-ai-to-doubt-its-own-plan/</link><guid isPermaLink="false">3dbceba4-628b-485e-8b51-e5235426d5bb</guid><category><![CDATA[ai]]></category><category><![CDATA[agentic]]></category><category><![CDATA[claude]]></category><category><![CDATA[copilot]]></category><category><![CDATA[context]]></category><category><![CDATA[plan]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Tue, 12 May 2026 21:10:27 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2026/05/agentic-reflexion-hero-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2026/05/agentic-reflexion-hero-2.png" alt="Agentic Reflexion: Teach Your AI to Doubt Its Own Plan"><p><strong>Your AI wrote a great plan. It's probably wrong. Here's how to catch that before you build.</strong></p>

<pre><code>        ┌──────────────────────────────────────────────┐
        │                                              │
   You  │                                         refine
    │   │                                              │
    ▼   ▼                                              │
┌─────────────────────┐                               │
│      Plan Mode      │  ← Model A (e.g., Sonnet)     │
│  (Read · Reason ·   │                               │
│    Propose)         │                               │
└─────────────────────┘                               │
           │                                          │
           │  Draft Plan                              │
           ▼                                          │
┌─────────────────────┐                               │
│    Plan Reviewer    │  ← Model B (e.g., Opus)       │
│  (Critique · Flag · │    Fresh context              │
│    Rank by Risk)    │    Adversarial prompt         │
└─────────────────────┘                               │
           │                                          │
    ┌──────┴──────┐                                   │
    │  Blockers?  │ ──── yes ─────────────────────────┘
    └──────┬──────┘
           │ no
           ▼
┌─────────────────────┐
│      Implement      │
└─────────────────────┘
</code></pre>

<p>There's a failure mode buried inside plan mode that nobody talks about.</p>

<p>The model that wrote the plan is also the most likely to think the plan is good. It built the reasoning. It owns the assumptions. When you ask the same model to review its own output — same context, same priors — it's not reviewing. It's confirming.</p>

<p>This is a well-known problem in AI research. The paper that formalised it — <a href="https://arxiv.org/abs/2303.11366"><em>Reflexion</em></a> (Shinn et al., 2023) — showed that giving agents a verbal feedback loop dramatically improved task performance. The core insight: critique before committing to an action is structurally better than acting and recovering. The catch: the critique has to be genuine. A self-reinforcing loop is worse than no critique at all, because it gives you false confidence.</p>

<p>The fix is surprisingly simple. You use a second agent — different model, fresh context, adversarial instructions — whose only job is to find what's wrong with the plan before you build it.</p>

<p>In this post you'll learn:</p>

<ul>
<li>What the Reflexion pattern is and why it matters for agentic coding</li>
<li>How plan mode fits into the loop — and where it falls short on its own</li>
<li>How to build a plan reviewer sub-agent that actually finds problems</li>
<li>Why the model family you pick for the reviewer changes what it catches</li>
<li>How GitHub Copilot CLI validated this exact pattern with their Rubber Duck feature — and the numbers behind it</li>
</ul>

<blockquote>
  <p>💡 This is <strong>Part 7</strong> in my Context Engineering series. <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a> covered the four pillars. <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a> introduced agent skills. <a href="https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/">Part 3</a> argued for lean over Scrum. <a href="https://blog.shanelee.name/2026/02/03/agentic-coding-git-worktrees-and-agent-skills-for-parallel-workflows/">Part 4</a> put skills to work with git worktrees. <a href="https://blog.shanelee.name/2026/02/24/markdown-never-left-ai-just-caught-up/">Part 5</a> showed why Markdown is the native format of agentic context. <a href="https://blog.shanelee.name/2026/03/22/spec-driven-development-is-waterfall-in-a-trench-coat-2/">Part 6</a> explained why spec-driven development is waterfall in a trench coat.</p>
</blockquote>

<hr>

<h2 id="thereflexionpattern">🧠 The Reflexion Pattern</h2>

<p>The original Reflexion paper introduced a deceptively simple idea: rather than asking an agent to act directly on a task, give it a loop.</p>

<ol>  
<li><strong>Generate</strong> — the agent produces an output (a plan, a solution, an action)</li>  
<li><strong>Evaluate</strong> — a critic assesses that output against some standard</li>  
<li><strong>Refine</strong> — the agent incorporates the critique and produces a better output</li>  
</ol>

<p>Repeat until good enough or resource limits are hit.</p>

<p>What made the results striking wasn't the loop itself — iteration is obvious. It was the quality of the critic. When the evaluator had a fresh context and adversarial instructions, agents caught mistakes they repeatedly missed on direct attempts. The same model could find what it wouldn't have generated.</p>

<p>The lesson for agentic coding isn't that you need a research paper's setup — it's that a second perspective, properly framed, is structurally different from self-review. The critique has to come from somewhere the original reasoning didn't.</p>

<hr>

<h2 id="planmodethegeneratorhalf">📋 Plan Mode: The Generator Half</h2>

<p>If you've used Claude Code's plan mode, you've seen the first half of the Reflexion loop already.</p>

<p>You describe the task. Claude Code reads your codebase — your CLAUDE.md, the relevant files, the existing patterns — and produces a structured plan before touching anything. It identifies which files change, what functions are affected, what the implementation sequence looks like, and flags anything it's uncertain about.</p>

<p>This is genuinely valuable. It surfaces the reasoning before you're committed to it. You can redirect, push back, or add constraints before a single line of code is written. That's the right gate.</p>

<p>But plan mode has a blind spot. The model that wrote the plan has been <em>primed</em> to think it's correct. It read your codebase through the lens of solving your task. Its context window is full of reasoning that supports the approach it just proposed. Asking that same context to critique the plan is like asking someone to proofread their own essay five minutes after writing it — the eye slides over what the brain already decided was fine.</p>

<p>What you need is a second reader who wasn't in the room when the plan was written.</p>

<hr>

<h2 id="entertheskeptictheplanreviewersubagent">🔴 Enter the Skeptic: The Plan Reviewer Sub-Agent</h2>

<p>The plan reviewer is a sub-agent whose only job is to find problems.</p>

<p>Not to validate the approach. Not to suggest alternatives. Not to summarise what the plan does well. To find what will break it.</p>

<p>Here's what the output looks like in practice:</p>

<pre><code>## Plan Review

### Blockers
- Step 3 calls `migrate_schema()` but this function does not exist in the codebase.
  Grep confirms no match. Plan assumes it was already implemented in a prior ticket.

### Significant Risks  
- The migration has no rollback step. If the schema change fails mid-run on the
  50M-row table, there is no documented recovery path.

### Minor Concerns
- Step 7 says "update accordingly" — underspecified. Which callers are affected
  and in what order?
</code></pre>

<p>That's the output you want <em>before</em> implementation, not after. The blocker in that example — calling a function that doesn't exist — would have caused an immediate runtime failure. The plan reviewer caught it in thirty seconds by grepping the codebase. The planning model didn't, because it assumed the function was there.</p>

<p>In practice, a single review pass catches the high-severity issues. If the plan changes substantially after refinement, run the reviewer again — a revised plan can introduce new assumptions just as easily as the original.</p>

<hr>

<h2 id="whythemodelfamilymatters">🎯 Why the Model Family Matters</h2>

<p>The single most important decision in setting up a plan reviewer is <em>which model it uses</em>.</p>

<p>This isn't about one model being "better" than another in some general sense. It's about cognitive diversity. Different model families have different training, different blind spots, different tendencies toward optimism or caution. A plan generated by Claude Sonnet reviewed by Claude Sonnet in the same session is likely to surface the same risk profile the plan itself assumed.</p>

<p>A plan generated by Sonnet, then passed to Opus in a fresh context window with adversarial instructions, is different. Opus has different priors about what constitutes a risk. It hasn't been warmed up by the same planning conversation. It's reading the plan cold, the same way a senior engineer would if you dropped it in their inbox.</p>

<p>The principle generalises beyond Claude. If your planning agent uses GPT-4o, review with Claude. If it uses Claude Sonnet, review with Opus. The point isn't that one is smarter — it's that the review is structurally independent. You're not asking the same chain of reasoning to audit itself.</p>

<blockquote>
  <p>💡 <strong>Model diversity in the review step is the difference between a rubber stamp and a genuine gate.</strong></p>
</blockquote>

<hr>

<h2 id="theindustryagreescopilotsrubberduck">🦆 The Industry Agrees: Copilot's Rubber Duck</h2>

<p>This isn't a niche pattern. GitHub just shipped exactly this in <a href="https://github.blog/ai-and-ml/github-copilot/github-copilot-cli-combines-model-families-for-a-second-opinion/">GitHub Copilot CLI</a> under the name <strong>Rubber Duck</strong> — and the numbers make the case better than theory does.</p>

<p>When you select Claude as the orchestrator in Copilot CLI, Rubber Duck automatically brings in GPT-5.4 as the reviewer. Cross-model by design. The system fires at three checkpoints:</p>

<ol>  
<li><strong>After planning</strong> — catches suboptimal early decisions before they compound</li>  
<li><strong>After complex implementation</strong> — identifies edge cases in intricate code</li>  
<li><strong>After test writing</strong> — flags coverage gaps before execution</li>  
</ol>

<p>The performance result is striking. Testing on SWE-Bench Pro found that <strong>Claude Sonnet 4.6 paired with Rubber Duck running GPT-5.4 closed 74.7% of the performance gap between Sonnet and Opus</strong> — approaching Opus-level resolution rates at Sonnet cost. On the hardest multi-file problems, the pairing scored 4.8% higher than the baseline.</p>

<p>The bugs Rubber Duck caught in real-world testing are instructive too: a scheduler that immediately exited due to an architectural flaw, silent data loss from overwritten dictionary keys, cross-file conflicts breaking Redis references. These aren't hypothetical risks — they're the exact category of problem that compiles, passes linting, and only breaks in production.</p>

<blockquote>
  <p>💡 You don't get near-Opus performance by using a better model. You get it by adding a cross-model reviewer to your loop.</p>
</blockquote>

<p>The name is a deliberate nod to the classic <a href="https://en.wikipedia.org/wiki/Rubber_duck_debugging">rubber duck debugging</a> technique — explaining your code to an inanimate object to surface problems you couldn't see while writing. The insight is the same: a second perspective that wasn't involved in producing the output finds what you normalised away.</p>

<p>GitHub built it into the tool. You can build it into your workflow today with a single agent definition.</p>

<hr>

<h2 id="buildingtheplanreviewer">⚙️ Building the Plan Reviewer</h2>

<p>You don't need a complex setup to implement this. In Claude Code, a plan reviewer is a standard agent definition — a markdown file in your <code>.agents/agents/</code> directory with a system prompt and a model assignment.</p>

<p>The key elements:</p>

<p><strong>1. Make it adversarial by default.</strong> The reviewer's job is to find problems, not to acknowledge what the plan does right. Any praise is wasted context.</p>

<p><strong>2. Give it verification tools.</strong> The most common plan failure is an assumption about the codebase that turns out to be false. The reviewer needs <code>Read</code>, <code>Grep</code>, and <code>Glob</code> access to check those assumptions against actual code — not just against what the plan claims.</p>

<p><strong>3. Order findings by severity.</strong> A list of concerns is useful. A list ordered by blast radius is actionable. Blockers (will cause failure) first, then significant risks (likely to cause production issues), then minor concerns (underspecified steps, optimistic assumptions).</p>

<p><strong>4. Make it trigger proactively.</strong> The description field determines when Claude Code invokes it automatically. Phrase it to trigger after any plan is produced, not just when explicitly asked — because the cases where you forget to ask are exactly the cases that go wrong.</p>

<p><strong>5. Set <code>color: red</code>.</strong> It's a small touch, but it visually marks the reviewer as the adversarial voice in your agent list — a useful reminder that its job is to find fault, not to help.</p>

<p>Here's the full definition from my <a href="https://github.com/shavo007/langchain-anthropic-pdf-support"><code>langchain-anthropic-pdf-support</code></a> project:</p>

<pre><code class="language-markdown">---
name: plan-reviewer  
description: |  
  Use this agent when a plan has been written and needs critical review before
  implementation begins. Trigger proactively after producing an implementation plan,
  migration plan, refactor proposal, or architectural design. Also trigger when
  the user asks to "review the plan", "critique this approach", or "find problems
  with this".
model: opus  
color: red  
tools: ["Read", "Grep", "Glob"]  
---

You are a principal engineer reviewing a plan. Your job is to find problems,  
not validate assumptions.

Systematically identify:  
1. Edge cases not accounted for  
2. Performance implications (N+1, blocking, scale issues)  
3. Backwards compatibility risks  
4. Missing rollback steps  
5. Anything underspecified or optimistic

Verify plan assumptions against actual code using Read/Grep/Glob.  
Order findings by severity: blockers first, then significant risks,  
then minor concerns.  
Do not praise or summarise what the plan does well.  
</code></pre>

<blockquote>
  <p><strong>Note on <code>model: opus</code>:</strong> The repo uses <code>model: inherit</code> by default, which picks up whatever model the parent session is running. For the reviewer to provide genuine cognitive diversity, set it explicitly — <code>opus</code> if you're running Sonnet day-to-day, or swap model families entirely if you're on a non-Claude tool.</p>
</blockquote>

<p><strong>If you're not on Claude Code</strong>, the same principle applies. In Cursor, add a rule in <code>.cursorrules</code> that instructs a second agent pass to critique the plan before acting. In GitHub Copilot CLI, Rubber Duck does this automatically. The pattern is tool-agnostic — what matters is the structural separation: a different model, a fresh context, and an adversarial framing.</p>

<hr>

<h2 id="thebottomline">The Bottom Line</h2>

<p>Plan mode is valuable. But a plan reviewed only by the model that wrote it is a plan that's checked its own homework.</p>

<p>The Reflexion pattern gives you a genuine quality gate: a second model, fresh context, explicitly tasked with finding what the plan got wrong before you build it. The reviewer reads cold. It verifies assumptions against real code. It ranks what it finds by severity so you know what to fix before you act.</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 6px 0;">✅ <strong>Use plan mode</strong> to generate a structured plan before any implementation</li>
  <li style="padding: 6px 0;">✅ <strong>Spawn a plan reviewer agent</strong> with a different model family and adversarial instructions</li>
  <li style="padding: 6px 0;">✅ <strong>Give the reviewer code access</strong> so it can verify assumptions, not just critique the document</li>
  <li style="padding: 6px 0;">✅ <strong>Trigger it proactively</strong> — the cases where you forget to ask are exactly the cases that go wrong</li>
  <li style="padding: 6px 0;">✅ <strong>Re-run after significant refinement</strong> — a revised plan can introduce new assumptions just as easily as the original</li>
  <li style="padding: 6px 0;">❌ <strong>Don't use the same model and context</strong> for both planning and review — you're not getting a second opinion, you're getting a confirmation</li>
</ul>

<p>The plan reviewer doesn't replace judgment. You still decide which findings to act on. But it gives you something plan mode can't: a perspective that wasn't in the room when the plan was written.</p>

<p>That's the difference between a plan you reviewed and a plan that's been stress-tested.</p>]]></content:encoded></item><item><title><![CDATA[Spec-Driven Development is Waterfall With a Fresh Coat of Paint]]></title><description><![CDATA[<p><strong>The AI tooling ecosystem keeps rediscovering waterfall. Here's why you should resist it.</strong></p>

<p>When did you last finish a requirements document and think: <em>yes, this is exactly what we'll build, and it'll still be accurate when we ship</em>?</p>

<p>If you've been in software long enough, you already know the answer.</p>]]></description><link>https://blog.shanelee.name/2026/03/22/spec-driven-development-is-waterfall-in-a-trench-coat-2/</link><guid isPermaLink="false">74b812f0-6b4d-4fb0-9ff7-db96cac8b523</guid><category><![CDATA[ai]]></category><category><![CDATA[claude]]></category><category><![CDATA[copilot]]></category><category><![CDATA[llm]]></category><category><![CDATA[agentic]]></category><category><![CDATA[markdown]]></category><category><![CDATA[context]]></category><category><![CDATA[spec]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Sun, 22 Mar 2026 21:51:02 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2026/03/spec-driven-dev-hero-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2026/03/spec-driven-dev-hero-2.png" alt="Spec-Driven Development is Waterfall With a Fresh Coat of Paint"><p><strong>The AI tooling ecosystem keeps rediscovering waterfall. Here's why you should resist it.</strong></p>

<p>When did you last finish a requirements document and think: <em>yes, this is exactly what we'll build, and it'll still be accurate when we ship</em>?</p>

<p>If you've been in software long enough, you already know the answer. The spec is wrong the moment you stop writing it. Software is discovery — you learn what the right solution is by building it, not by describing it in advance.</p>

<p>So why is the AI tooling industry trying so hard to bring specs back?</p>

<p>In this post you'll learn:</p>

<ul>
<li>What spec-driven development is and which tools are pushing it</li>
<li>Why it's structurally identical to waterfall — just with better marketing</li>
<li>Why an AI-empowered team with agentic context engineering outships a spec-heavy team every time</li>
</ul>

<blockquote>
  <p>💡 This is <strong>Part 6</strong> in my Context Engineering series. <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a> covered the four pillars. <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a> introduced agent skills. <a href="https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/">Part 3</a> argued for lean over Scrum. <a href="https://blog.shanelee.name/2026/02/03/agentic-coding-git-worktrees-and-agent-skills-for-parallel-workflows/">Part 4</a> put skills to work with git worktrees. <a href="https://blog.shanelee.name/2026/02/24/markdown-never-left-ai-just-caught-up/">Part 5</a> showed why Markdown is the native format of agentic context.</p>
</blockquote>

<hr>

<h2 id="whatisspecdrivendevelopment">🔍 What is Spec-Driven Development?</h2>

<p>The premise sounds reasonable: before asking AI to write code, write a detailed specification. The AI reads the spec, understands the intent, and generates an implementation that matches it. No vague prompts. No hallucinated architecture. Just a clear document that drives predictable output.</p>

<p>Several tools and frameworks have formalised this into a full workflow.</p>

<p><strong>OpenSpec</strong> is probably the most talked-about right now. It installs via npm and works across Claude Code, Cursor, GitHub Copilot, and 30+ other tools. When you run <code>/openspec:proposal</code>, it generates a structured change proposal — requirements doc, implementation tasks, design decisions — before a single line of code is written. It markets itself as "lightweight" and "brownfield-first," and to be fair, it's more considered than a blank requirements doc. But the fundamental shape is the same: spec first, code second.</p>

<hr>

<h2 id="itswaterfallherestheproof">💧 It's Waterfall. Here's the Proof.</h2>

<p>Let's put the two workflows side by side.</p>

<p>Classic waterfall:</p>

<ol>  
<li>Requirements phase → document everything</li>  
<li>Design phase → spec everything</li>  
<li>Implementation phase → build to spec</li>  
<li>Testing phase → verify against spec</li>  
<li>Release</li>  
</ol>

<p>Spec-driven AI development:</p>

<ol>  
<li>Discovery / inception → gather requirements</li>  
<li>Write spec (BA, PM, or AI-assisted) → document everything</li>  
<li>AI implements to spec</li>  
<li>Review and validate</li>  
</ol>

<p>Same shape. Different tools.</p>

<p>The spec-driven workflow also sneaks back in the roles that lean teams have been actively trying to eliminate:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 6px 0;">📋 <strong>Business Analysts</strong> to write the requirements</li>
  <li style="padding: 6px 0;">✍️ <strong>Product Owners</strong> to sign off on the spec</li>
  <li style="padding: 6px 0;">🏛️ <strong>Architects</strong> to validate it's implementable</li>
  <li style="padding: 6px 0;">🔍 <strong>Dedicated QA</strong> to verify against the spec</li>
</ul>

<p>These are hand-off roles. Each one is a seam where context gets lost, where timelines stretch, and where "it wasn't in the spec" becomes the answer to every inconvenient question.</p>

<p>This isn't a new failure mode. Behaviour-Driven Development made the same bet a decade ago — that BAs and product owners could write tests in Gherkin, the human-readable format behind tools like Cucumber, without deep engineering involvement. The promise was that non-technical stakeholders could define acceptance criteria directly. In practice, the Gherkin files were either written by engineers anyway, or they drifted from reality the moment the code changed. The handover assumption was the problem then, and it's the problem now.</p>

<p>Modern testing has moved in the opposite direction. Shift-left testing brings quality thinking into the development loop rather than delegating it downstream. Test automation tooling — MCPs and CLIs like Playwright — lets engineers write and run tests as part of the build cycle, not as a separate gate. The assumption that non-technical people should own the test layer was always backwards. What works is engineers who think about quality from the start.</p>

<p>This is about more than methodology. It's about team composition. In the AI era, the answer isn't a better handover process — it's not needing one. What you need are AI-empowered engineers with strong engineering instincts and the systems thinking to own a problem end to end.</p>

<p>But there's a subtler problem beneath the process overhead: the methodology and the headcount justify each other in a loop that's very hard to break from the inside. You need BAs to write the specs. You need QAs to validate against them. You need sign-off ceremonies before work begins and acceptance cycles before it ships. <mark>Heavyweight spec processes don't just slow teams down — they create an organisational incentive where writing a good spec becomes more valued than shipping good software.</mark></p>

<p>And here's the deeper problem: spec-driven development is solving the wrong bottleneck. Generating code was never the problem. Deciding <em>what</em> to build — and staying adaptable as that changes — is the hard part. A spec locks you into an answer before you've done the learning.</p>

<blockquote>
  <p>💡 The future is T-shaped engineers who can own the full problem. Not specialists producing hand-off artefacts.</p>
</blockquote>

<p>And there's another problem: spec rot. The spec is a snapshot of what you thought you needed at the moment you wrote it. The moment you start building, reality diverges. You're now maintaining two artefacts — the spec and the code — and they drift apart from day one.</p>

<p>No feedback loop in the build means you don't discover what's wrong with your design until you've already built to it. That's exactly waterfall's fatal flaw — and spec-driven development inherits it wholesale.</p>

<hr>

<h2 id="hackernewscalleditimmediately">🚨 Hacker News Called It Immediately</h2>

<p>I'm not alone in this read. When marmelab published a critique of spec-driven development on Hacker News, it hit 225 points and 191 comments — and the title said it all: <a href="https://marmelab.com/blog/2025/11/12/spec-driven-development-waterfall-strikes-back.html"><em>Spec-Driven Development: The Waterfall Strikes Back</em></a>.</p>

<p>The thread reactions were sharp:</p>

<blockquote>
  <p><em>"Managers appreciate SDD for the same reasons they favored waterfall initially."</em> — constantcrying</p>
  
  <p><em>"The distinction between waterfall and agile is merely how much spec you write before implementing — longer feedback loops increase risk of major mistakes."</em> — pydry</p>
  
  <p><em>"Really, we are doing waterfall, but with AI, now?"</em> — multiple commenters</p>
</blockquote>

<p>Engineers who tried Kiro — AWS's spec-driven agentic IDE — were equally blunt:</p>

<blockquote>
  <p><em>"Kiro generated massive task lists (12+ tasks with 4+ sub-tasks each) with unpredictable code deletion — a sledgehammer to crack a nut."</em> — hatmanstack</p>
  
  <p><em>"Kiro, your new corporate project manager."</em> — iamsaitam (on Kiro treating a small bug fix as a full project management exercise)</p>
</blockquote>

<p>When you show experienced engineers a spec-driven workflow, the reaction is consistent regardless of platform: <em>we tried this already</em>.</p>

<hr>

<h2 id="therightcontextislightweightnotheavyweight">⚡ The Right Context is Lightweight, Not Heavyweight</h2>

<p>Here's what I think people are actually reaching for when they advocate for spec-driven development: <strong>they want AI to have enough context to produce good output</strong>. That's a legitimate goal. The mistake is thinking a formal spec document is the right way to deliver it.</p>

<p>The Agile Manifesto articulated this argument in 2001:</p>

<blockquote>
  <p><em>"Working software over comprehensive documentation."</em></p>
</blockquote>

<p>25 years later, spec-driven AI development is rediscovering the same mistake the Manifesto was written to correct.</p>

<p>What does good agentic context engineering actually look like? It's three things:</p>

<p><strong>1. Context files</strong> — always-on institutional knowledge that lives in the repo, versioned with your code. <code>CLAUDE.md</code> covers project conventions, architecture decisions, gotchas, and commands. <code>agents.md</code> (covered in <a href="https://blog.shanelee.name/2026/02/24/markdown-never-left-ai-just-caught-up/">Part 5</a>) gives the agent ground truth about your agent architecture — what each agent does, how they relate, known quirks. Neither of these is a spec. They describe what <em>is</em>, not what <em>should be</em>. Both are plain Markdown, readable by any agentic tool, owned entirely by your team.</p>

<p><strong>2. Plan mode</strong> — an interactive dialogue with the agent to scope the work before acting. Not a document. Not a hand-off artefact. A conversation. Here's what that looks like in practice:</p>

<p>You type: <em>"Add user notification preferences to the API — get and set per-user settings."</em></p>

<p>Claude Code reads your codebase and responds in under a minute: <em>"I can see your Express router in <code>src/api/routes.js</code>. I'll add <code>GET /users/:id/preferences</code> and <code>PATCH /users/:id/preferences</code>, extend the User model with a JSON preferences column, and add a migration. I'll reuse your existing auth middleware. Want to proceed, or adjust anything?"</em></p>

<p>You refine. You ship. The spec-driven equivalent of that exchange is a requirements document, a BA-facilitated discovery session, an architect sign-off, and a sprint of writing before anyone touches the code.</p>

<p>The reason plan mode works is the same reason context engineering works — you're doing the thinking that a spec tries to capture, but you're doing it in a living, adaptive way rather than freezing it in a document that will be out of date before the first PR is merged. The plan evolves with the codebase. The context evolves with understanding. The output is software, not documentation.</p>

<p>Plan mode produces a <code>plan.md</code> — a structured, readable artefact you can review, edit, and pass to a sub-agent. Before execution I run a principal engineer review: a sceptical sub-agent persona whose job is to find problems with the plan before a line of code is written. A fresh context window sees what your planning session normalised. This is the quality gate — lightweight, fast, and embedded in the loop rather than bolted on at the end as a separate role.</p>

<p>OpenSpec's own FAQ raises this objection: <em>"Plan mode is great for a single chat session — but what about longer work that spans multiple sessions?"</em> Their answer is a spec. The actual answer is simpler: a <code>progress.md</code> file. The agent maintains it as work proceeds — completed steps, decisions made, what's next. At the start of the next session it reads its own notes and picks up where it left off. You don't need a platform, a structured workflow, or a vendor's artefact format. You need a text file and a habit.</p>

<p><strong>3. A prototype</strong> — build something real, show it to a stakeholder, learn. The fastest way to discover what you need to build is to build it.</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Spec-Driven Development</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Plan Mode (Claude Code)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Written upfront, before building</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Created interactively, just before acting</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Formal document</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Conversation-first, optionally captured</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Requires dedicated roles (BA, PM, architect)</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">One T-shaped engineer</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Locked in before learning</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Revised as understanding improves</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Spec → code (one-way)</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Plan → code → refine → code (iterative)</td>
    </tr>
  </tbody>
</table>

<hr>

<h2 id="theteamyouactuallyneed">👥 The Team You Actually Need</h2>

<p>In <a href="https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/">Part 3</a>, I argued that Scrum's ceremony overhead doesn't survive contact with AI development speeds. Spec-driven development has the same problem, and it introduces an additional one: it justifies bloated team structures.</p>

<p>If your workflow requires someone to write the spec, someone to implement it, someone to verify it, and someone to maintain it as it drifts — you've described a four-person process for work one T-shaped engineer with an AI agent could handle in an afternoon. (A T-shaped engineer is someone with deep expertise in one area and enough breadth to own the problem end to end — from requirement clarity to production — without a handoff chain.)</p>

<p>Importantly, the BA and QA functions don't disappear in this model — they just stop being separate roles with separate artefacts and handoff ceremonies. A strong engineer with good AI tooling and a disciplined approach to context engineering can own requirement clarity as part of how they think about a problem, not as a separate phase before they're allowed to start. The translation between business need and technical implementation is a skill, not a job title. Engineers who develop that skill — and AI tooling makes it significantly easier — are more valuable than engineers who hand that responsibility to a BA and wait.</p>

<p>The same applies to quality. Quality is not a function — it's a standard. When it's owned by a specialist role, it becomes that role's problem to manage rather than every engineer's responsibility to maintain. When quality thinking is embedded in the agentic development loop rather than delegated to a gate at the end, failure rates don't increase. In many cases they improve, because the person closest to the code is also closest to the risk.</p>

<p>The Claude Code team at Anthropic is a well-documented example of this model working at scale. They built one of the most serious AI coding tools in the industry without traditional PRDs — using plan mode and rapid prototyping instead.</p>

<p>That's the team composition the AI era calls for:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 6px 0;">🔷 <strong>Flat</strong> — minimal hierarchy, no specialist silos</li>
  <li style="padding: 6px 0;">🔷 <strong>T-shaped</strong> — deep in one area, broad enough to own the full stack</li>
  <li style="padding: 6px 0;">🔷 <strong>Outcome-oriented</strong> — shipping working software, not producing documents</li>
  <li style="padding: 6px 0;">🔷 <strong>AI-empowered</strong> — each engineer multiplied by an agent</li>
</ul>

<p>In <a href="https://newsletter.pragmaticengineer.com/p/building-claude-code-with-boris-cherny">an interview with The Pragmatic Engineer</a>, Boris Cherny — who led the development of Claude Code — put it plainly:</p>

<blockquote>
  <p><em>"There's just no way we could have shipped this if we started with static mocks and Figma or if we started with a PRD."</em></p>
</blockquote>

<p>If the teams building the tools aren't writing PRDs, the argument that PRDs are necessary for serious product development becomes very hard to sustain. The question isn't whether this model scales — the question is whether your organisation has the hiring standards and culture to make it work.</p>

<hr>

<h2 id="thebottomline">The Bottom Line</h2>

<p>Every generation of tooling rediscovers the same heavyweight process and gives it a new name. Waterfall became RUP became Scrum became SAFe. Now it's spec-driven development. The values the Agile movement fought for — working software over comprehensive documentation, responding to change over following a plan — are exactly what spec-driven development trades away.</p>

<p>You don't need a spec. You need good agentic context engineering principles — CLAUDE.md, plan mode, T-shaped engineers, prototypes. What you don't need:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 6px 0;">❌ A BA writing requirements before the team has touched the problem</li>
  <li style="padding: 6px 0;">❌ A spec document to maintain in lockstep with your code</li>
  <li style="padding: 6px 0;">❌ An inception workshop before a single line is written</li>
</ul>

<p>Spec-driven development frameworks are solving a problem that better hiring, better tooling, and better engineering culture have already made obsolete. They're not a step forward — they're a formalisation of constraints that high-performing teams have already moved past.</p>

<p>The spec was never the source of truth. The software always was.</p>]]></content:encoded></item><item><title><![CDATA[Markdown Never Left — AI Just Caught Up]]></title><description><![CDATA[<p><strong>It's not even a technology. It's a convention. And that's exactly why it wins.</strong></p>

<p>Markdown is a writing convention — a set of rules for formatting plain text that John Gruber wrote a spec for in 2004. No runtime. No SDK. No API. Just text with agreed-upon symbols that any human</p>]]></description><link>https://blog.shanelee.name/2026/02/24/markdown-never-left-ai-just-caught-up/</link><guid isPermaLink="false">3c8978b1-d0c2-499f-b6c3-4bc21711e365</guid><category><![CDATA[ai]]></category><category><![CDATA[llm]]></category><category><![CDATA[claude]]></category><category><![CDATA[copilot]]></category><category><![CDATA[mcp]]></category><category><![CDATA[skills]]></category><category><![CDATA[agentic]]></category><category><![CDATA[markdown]]></category><category><![CDATA[tokens]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Tue, 24 Feb 2026 20:27:07 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2026/02/langchain-migration-hero-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2026/02/langchain-migration-hero-1.png" alt="Markdown Never Left — AI Just Caught Up"><p><strong>It's not even a technology. It's a convention. And that's exactly why it wins.</strong></p>

<p>Markdown is a writing convention — a set of rules for formatting plain text that John Gruber wrote a spec for in 2004. No runtime. No SDK. No API. Just text with agreed-upon symbols that any human or machine already understands. While the AI industry has shipped transformers, vector databases, multi-agent orchestration frameworks, and billion-parameter models, Markdown has just quietly remained what it always was: the lowest common denominator of structured information.</p>

<p>And that turns out to be exactly what agentic context engineering needs.</p>

<p>I've been a Docs as Code person for years. Markdown in version control, reviewed in PRs, rendered by static site generators — it's the default format for everything I write. When I started building agentic workflows, that habit turned out to be directly valuable in a way I hadn't anticipated. The contrast isn't simple technology versus complex technology. It's a convention versus the most sophisticated AI systems ever built — and the convention wins on cost, portability, and clarity every time.</p>

<p>This post is about that insight — and how it played out across every stage of a real migration: upgrading five LangChain agents from 0.3 to 1.2. The migration is the example. Markdown is the point.</p>

<p>In this post you'll learn:</p>

<ul>
<li>Why Markdown is the highest-signal, most token-efficient format for AI context — and the numbers that prove it</li>
<li>How Docs as Code thinking (MkDocs, Backstage, Marp) maps directly onto agentic context engineering</li>
<li>Three ways to get any documentation into your context window as Markdown automatically</li>
<li>How every artefact in an agentic workflow — context files, plans, reviews, progress logs — is Markdown doing its job</li>
<li>Why Markdown files that survive context resets are the key to multi-day agentic tasks</li>
</ul>

<blockquote>
  <p>💡 This is <strong>Part 5</strong> in my Context Engineering series. <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a> covered the four pillars. <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a> introduced agent skills. <a href="https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/">Part 3</a> argued for lean over Scrum. <a href="https://blog.shanelee.name/2026/02/03/agentic-coding-git-worktrees-and-agent-skills-for-parallel-workflows/">Part 4</a> put skills to work with Git worktrees.</p>
</blockquote>

<hr>

<h2 id="markdownascontextwhyformatmattersforai">📖 Markdown as Context: Why Format Matters for AI</h2>

<p>I've been using Markdown for years — not just as a formatting convenience, but as a philosophy. <strong>Docs as Code</strong>: documentation that lives in version control, travels with your codebase, and gets reviewed alongside your PRs. ADRs (Architectural Decision Records) capturing why a technical choice was made. RFCs proposing and debating changes before implementation. And this blog — written in Markdown, published via Ghost, specifically because Ghost keeps it simple and stays out of the way. If you've worked with <a href="https://www.mkdocs.org/">MkDocs</a> to build static documentation sites, or <a href="https://backstage.io/docs/features/techdocs/">Backstage's TechDocs</a> — which uses Markdown as the foundation for an entire developer portal — you already know the power of treating docs as first-class artefacts. Even presentations: <a href="https://marpit.marp.app/">Marp</a> turns Markdown into slides, which means I can write a deck in the same format as everything else. Markdown all the way down.</p>

<p>So when I'm handing context to an AI agent, Markdown is the obvious format. It's not a new habit — it's the same discipline I've used for years, applied to a new domain. The difference is that with AI, the choice of format carries a measurable, direct cost.</p>

<p>When you're migrating across major library versions, your agent needs documentation — lots of it. Release notes, migration guides, changelogs, API references. The naïve approach is to point your agent at the docs website and let it browse. The problem: documentation websites are HTML, and HTML is expensive.</p>

<p>Cloudflare put some hard numbers on this recently. Their <a href="https://blog.cloudflare.com/markdown-for-agents/">Markdown for Agents</a> post measured a real documentation page: <strong>16,180 tokens as HTML, 3,150 tokens as markdown. An 80% reduction.</strong></p>

<p>Their framing nails it:</p>

<blockquote>
  <p><em>"Feeding raw HTML to an AI is like paying by the word to read packaging instead of the letter inside."</em></p>
</blockquote>

<p>A simple heading like <code>## About Us</code> costs 2–3 tokens in markdown. The equivalent HTML — <code>&lt;h2 class="section-title" id="about"&gt;About Us&lt;/h2&gt;</code> — runs 12–15. Every element, every wrapper div, every ARIA attribute, every script tag is a token your agent has to process without gaining any understanding of the actual content.</p>

<p>Markdown strips all of that. The same page in markdown: clean headings, code blocks, links. No wasted tokens. As Microsoft's <a href="https://github.com/microsoft/markitdown">MarkItDown</a> project puts it:</p>

<blockquote>
  <p><em>"Markdown conventions are also highly token-efficient."</em></p>
</blockquote>

<p>And LLMs don't just tolerate markdown — they're optimised for it. They were trained on vast quantities of it. Feeding your agent markdown isn't just cheaper; it's the format the model reasons about most fluently.</p>

<blockquote>
  <p><strong>Context windows are finite. Every token you waste on HTML scaffolding is a token you can't use for understanding your codebase.</strong></p>
</blockquote>

<h3 id="gettinglangchaindocsasmarkdown">Getting LangChain Docs as Markdown</h3>

<p>My first step was pulling down the LangChain release notes and migration guides as markdown — and it turns out the LangChain docs site makes this remarkably easy. Every page has a built-in <strong>"View as Markdown"</strong> option that returns the page as clean plain text, purpose-built for LLMs.</p>

<p><img src="https://blog.shanelee.name/content/images/2026/02/Screenshot-2026-02-24-at-20-52-05-1.png" alt="Markdown Never Left — AI Just Caught Up" view="" as="" markdown"="" and="" "copy="" mcp="" server"="" dropdown="" options"=""></p>

<p>The docs site also ships with a built-in MCP server. Hit the dropdown on any page and you'll see "Copy MCP Server" — one click gives you the config to paste directly into your <code>.mcp.json</code>:</p>

<pre><code class="language-json">{
  "mcpServers": {
    "langchain-docs": {
      "type": "http",
      "url": "https://docs.langchain.com/mcp"
    }
  }
}
</code></pre>

<p>Add that and your agent is connected to the full LangChain docs knowledge base — searchable, queryable, without fetching a single HTML page.</p>

<p>This is the ecosystem catching up to how agents actually work. More documentation sites are building markdown-first, AI-native access directly into their platforms — and when they do, use it.</p>

<h3 id="thecloudflaretrickmarkdownondemandforeverythingelse">The Cloudflare Trick: Markdown on Demand for Everything Else</h3>

<p>For documentation sites that <em>don't</em> have built-in markdown export, Cloudflare has shipped something genuinely useful. Their new <a href="https://blog.cloudflare.com/markdown-for-agents/">Markdown for Agents</a> feature lets any site served through Cloudflare return markdown instead of HTML — automatically, based on a single request header.</p>

<p>The mechanism is simple: your agent sends <code>Accept: text/markdown</code> in the HTTP request. Cloudflare intercepts it, converts the HTML to markdown in real time, and returns it. The response even includes an <code>x-markdown-tokens</code> header so you can see exactly how many tokens you're working with.</p>

<pre><code class="language-bash">curl -H "Accept: text/markdown" https://some-docs-site.com/page  
</code></pre>

<p><img src="https://blog.shanelee.name/content/images/2026/02/cloudflare-markdown-for-agents.png" alt="Markdown Never Left — AI Just Caught Up"></p>

<p>No conversion tooling required on your end. No scraping, no parsing. For any documentation that isn't LangChain — release notes, third-party libraries, framework guides — this is the most frictionless path to token-efficient context.</p>

<p>If you're using Claude Code, you don't need to do anything at all. I tested it: Claude Code's <code>WebFetch</code> tool sends <code>Accept: text/markdown, text/html, */*</code> as a request header automatically. Fetching the Cloudflare blog post itself returned:</p>

<pre><code>content-type: text/markdown; charset=utf-8  
x-markdown-tokens: 4023  
content-signal: ai-train=yes, search=yes, ai-input=yes  
</code></pre>

<p>Markdown served directly, token count included, usage permissions declared. No extra configuration. If the site is on Cloudflare and has the feature enabled, you're already getting the token savings just by using the built-in fetch tool.</p>

<h3 id="markitdownmcpthefallbackforeverythingelse">MarkItDown MCP: The Fallback for Everything Else</h3>

<p>For anything that's neither LangChain docs nor Cloudflare-hosted — PDFs, Word documents, downloaded changelogs — <a href="https://github.com/microsoft/markitdown">MarkItDown</a> has you covered. It's Microsoft's open-source Python tool that converts virtually any file format to clean markdown: HTML, PDF, Word, Excel, PowerPoint, and more.</p>

<p>Crucially, it ships with an official MCP server — <a href="https://github.com/microsoft/markitdown/tree/main/packages/markitdown-mcp"><code>markitdown-mcp</code></a> — so your agent can convert any file on demand without leaving the session.</p>

<pre><code class="language-bash">pip install markitdown-mcp  
</code></pre>

<p>Wire it into your MCP configuration and it's available immediately:</p>

<pre><code class="language-json">{
  "mcpServers": {
    "markitdown": {
      "command": "uvx",
      "args": ["--python", "3.13", "markitdown-mcp"]
    }
  }
}
</code></pre>

<p>I used this extensively throughout the migration. Whenever I hit a document that wasn't available as clean markdown — a PDF of the old API reference, a downloaded changelog, some Word doc from an internal wiki — MarkItDown MCP converted it in-session, inline, without me having to leave the conversation or run a separate script. It became the default fallback so reliably that I stopped thinking about it as a separate tool and started treating it as part of the context pipeline.</p>

<p>Between the three approaches — native markdown export (LangChain), <code>Accept: text/markdown</code> (Cloudflare), and MarkItDown MCP (everything else) — you can get any documentation into your context window in token-efficient form. The bottom line is the same in every case: your agent gets the content it needs, without the packaging it doesn't.</p>

<hr>

<h2 id="agenticplanmodebuildingthemigrationblueprint">🗺️ Agentic Plan Mode: Building the Migration Blueprint</h2>

<p>With the documentation loaded as markdown, the next step wasn't writing code — it was planning. This is where agentic plan mode earns its keep. And it's where you start to see that Markdown isn't just for docs fetching — it's the format of every artefact the agent produces and consumes.</p>

<h3 id="givingtheagentcodebasecontext">Giving the Agent Codebase Context</h3>

<p>Before entering plan mode, I made sure the agent had a solid understanding of the existing architecture via an <code>agents.md</code> file I maintain in the codebase. This is Docs as Code applied directly to AI context: a Markdown file, version-controlled alongside the code, that describes each of the five agents, what they do, how they relate to each other, their shared dependencies, and any known quirks.</p>

<pre><code class="language-markdown"># agents.md

## Agent Overview

This codebase contains five AI agents:

### 1. IngestAgent
Responsible for document ingestion and chunking...

### 2. RetrievalAgent
Handles vector search and context retrieval...

[etc.]
</code></pre>

<p>This kind of structured context file is critical. Without it, your agent has to reverse-engineer the architecture from code — which it can do, but it wastes time and tokens, and the inferences can be wrong. <code>agents.md</code> gives the agent ground truth about intent, not just implementation. It's also readable by humans, diffable in git, and costs a fraction of what an equivalent HTML document would. Markdown doing exactly what Markdown does best.</p>

<h3 id="theplanmodeconversation">The Plan Mode Conversation</h3>

<p>With the migration guides loaded as markdown and the codebase context established, I entered plan mode and walked through the migration with the agent. This wasn't just "here's what needs to change" — it was a structured conversation:</p>

<ul>
<li>What are the breaking changes between 0.3 and 1.2 that affect each agent?</li>
<li>What's the safest migration order given agent dependencies?</li>
<li>Where are the highest-risk changes?</li>
<li>What backwards compatibility concerns do we need to manage?</li>
</ul>

<p>Plan mode keeps the agent in a reasoning-and-documenting state rather than immediately writing code. The output was a <code>plan.md</code> — a structured, step-by-step migration plan covering each agent, the specific changes required, the order of operations, and the testing checkpoints.</p>

<p>This is the Markdown flywheel in action: you fed the agent Markdown documentation, and it produced a Markdown plan. The format flows through the entire pipeline. And because it's plain text in a file, it's reviewable, editable, and — crucially — portable to the next step.</p>

<hr>

<h2 id="theprincipalengineerreview">🔬 The Principal Engineer Review</h2>

<p>Here's the move that changed the quality of my migrations: before implementing anything, I spin up a sub-agent with a fresh context window to review the plan.</p>

<h3 id="whyaseparatecontextwindowmatters">Why a Separate Context Window Matters</h3>

<p>When you've been in a planning session for an hour, your agent has accumulated context — your assumptions, your framings, the reasoning path you took to arrive at the plan. That accumulated context can be a blind spot. The agent is unlikely to challenge the premises it helped establish.</p>

<p>A sub-agent starts fresh. It has no memory of the planning conversation. When you hand it the plan as a Markdown document and ask it to critique, it reads it the way a new engineer would read it — without the benefit of your shared assumptions, but also without the baggage of them.</p>

<p>This is where the portability of Markdown pays off directly. <code>plan.md</code> is a self-contained artefact. No conversation state, no special format — just a text file you paste in. The sub-agent gets everything it needs from the document itself.</p>

<p>I use a "principal engineer" sub-agent persona for this: an experienced, sceptical reviewer whose job is to find problems. The prompt is straightforward:</p>

<pre><code class="language-markdown">You are a principal engineer reviewing a migration plan.  
Your job is to find problems, not validate assumptions.  
Review the following plan and identify:

1. Edge cases not accounted for  
2. Performance implications  
3. Backwards compatibility risks  
4. Missing rollback steps  
5. Anything that seems underspecified or optimistic  
</code></pre>

<p>Then I paste in <code>plan.md</code> as the sole input.</p>

<h3 id="whatitcatches">What It Catches</h3>

<p>It found areas to improve — which is exactly what you want. A fresh context window will always see things the planning session normalised, and because the plan lives in a Markdown file, handing it over is as simple as pasting the document in.</p>

<hr>

<h2 id="progresstrackingacrossdays">📋 Progress Tracking Across Days</h2>

<p>Large migrations rarely happen in one sitting. I worked on this across several days in whatever time I had — and I was running it using the <a href="https://blog.shanelee.name/2026/02/03/agentic-coding-git-worktrees-and-agent-skills-for-parallel-workflows/">Git worktree agent skill</a> from Part 4, which keeps the migration isolated in its own branch while other work continues in parallel. That introduced a new problem: how does the agent pick up where it left off across sessions?</p>

<h3 id="theprogressmdpattern">The progress.md Pattern</h3>

<p>This is Docs as Code for agent state. Inspired by the note-taking patterns used in deep agent frameworks, I gave the agent a <code>progress.md</code> file to maintain alongside <code>plan.md</code>. The format is simple — the example below is illustrative, not the exact steps from my migration:</p>

<pre><code class="language-markdown"># Migration Progress

## Status: In Progress

## Completed
- [x] IngestAgent — LangChain imports updated
- [x] IngestAgent — Chain interface migrated to new API
- [x] IngestAgent — Tests passing

## In Progress
- [ ] RetrievalAgent — Updating retriever interface

## Pending
- [ ] RetrievalAgent — Memory migration
- [ ] SummaryAgent
- [ ] RouterAgent
- [ ] OutputAgent

## Notes
- Remember: shared utility in utils/chains.py updated once in IngestAgent step — do NOT repeat
- Existing memory objects in DB need migration script before deploying RetrievalAgent
</code></pre>

<p>At the start of each session, the agent reads <code>progress.md</code> to understand current state. As it completes steps, it updates the file. It also cross-references <code>plan.md</code> to ensure it stays on the planned path rather than improvising.</p>

<p>This is low-tech but extraordinarily effective. The agent doesn't need to re-understand the entire codebase at the start of each session — it reads its own notes and picks up exactly where it left off. The context window investment at the start of each session stays small. And it's version-controlled, human-readable, and costs almost nothing in tokens to load.</p>

<blockquote>
  <p>💡 <strong>Think of <code>progress.md</code> as your agent's working memory that persists across sessions.</strong> The context window resets; the file doesn't. This is the Docs as Code principle applied to agent state: if it matters, write it down in Markdown and commit it.</p>
</blockquote>

<p><img src="https://blog.shanelee.name/content/images/2026/02/progress-md-memory.png" alt="Markdown Never Left — AI Just Caught Up"></p>

<hr>

<h2 id="keytakeaways">🎯 Key Takeaways</h2>

<p>Agentic workflows feel complex until you notice the common thread running through every step: it's all Markdown. The documentation you load, the context files you maintain, the plans you generate, the reviews you pass between agents, the state you persist across sessions — Markdown all the way down. Here's what that looks like in practice:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📄 <strong>Markdown is 80% cheaper than HTML.</strong> Cloudflare measured this on a real documentation page: 16,180 tokens as HTML, 3,150 as Markdown. LangChain docs has native Markdown export and a built-in MCP server. Use <code>Accept: text/markdown</code> for Cloudflare-hosted sites. Use MarkItDown MCP for everything else. Every token you save on packaging is a token your agent can spend on your codebase.</li>
  <li style="padding: 8px 0;">📝 <strong>Your context files are Markdown too.</strong> <code>agents.md</code> gave the agent ground truth about the codebase architecture. It's version-controlled, human-readable, and token-efficient. Docs as Code isn't just for documentation sites — it's for everything you want your agent to know.</li>
  <li style="padding: 8px 0;">🗺️ <strong>Plans are Markdown artefacts.</strong> Agentic plan mode produces a <code>plan.md</code> you can read, edit, and hand to a sub-agent. The format flows through the entire pipeline: Markdown in, Markdown out.</li>
  <li style="padding: 8px 0;">🔬 <strong>Markdown makes context portable across agents.</strong> The principal engineer review works because <code>plan.md</code> is a self-contained document. No conversation state, no special format — just a text file passed to a fresh context window. A fresh context window is a fundamentally different perspective; it catches what your planning session normalised.</li>
  <li style="padding: 8px 0;">📋 <strong><code>progress.md</code> is your agent's external memory.</strong> The context window resets; the file doesn't. Markdown state files let your agent pick up exactly where it left off across sessions, across days, across whatever interruptions real work involves.</li>
</ul>

<p>The entire migration — five agents, a major version jump, breaking changes throughout — completed successfully. But the bigger insight isn't about LangChain. It's that Markdown, treated as a first-class engineering artefact, is what makes complex agentic workflows tractable. Token-efficient context in, structured context out. The format does more work than you'd expect.</p>

<hr>

<p><em>Enjoyed this? The full Context Engineering series:</em></p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📝 <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/"><strong>Part 1</strong></a> — The four pillars of context engineering</li>
  <li style="padding: 8px 0;">🛠️ <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/"><strong>Part 2</strong></a> — Agent skills and the Agentic AI Foundation</li>
  <li style="padding: 8px 0;">🏃 <a href="https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/"><strong>Part 3</strong></a> — Why AI-powered teams need lean, not Scrum</li>
  <li style="padding: 8px 0;">🌳 <a href="https://blog.shanelee.name/2026/02/03/agentic-coding-git-worktrees-and-agent-skills-for-parallel-workflows/"><strong>Part 4</strong></a> — Git worktrees and agent skills for parallel workflows</li>
</ul>]]></content:encoded></item><item><title><![CDATA[Agentic Coding: Git Worktrees and Agent Skills for Parallel Workflows]]></title><description><![CDATA[<p><strong>From solo agent to parallel powerhouse: how Git worktrees and agent skills turn your agentic coding setup into a multi-tasking machine</strong></p>

<hr>

<p>When you're using agentic coding tools seriously, you've hit this wall:</p>

<ul>
<li>🤖 Agent working on Feature A in one terminal</li>
<li>🤖 Need to start Feature B <em>right now</em></li>
<li>💥 Can't — same working</li></ul>]]></description><link>https://blog.shanelee.name/2026/02/03/agentic-coding-git-worktrees-and-agent-skills-for-parallel-workflows/</link><guid isPermaLink="false">ffb51db0-c9ea-4620-9abc-53c3b73ebbf9</guid><category><![CDATA[ai]]></category><category><![CDATA[claude]]></category><category><![CDATA[copilot]]></category><category><![CDATA[agentic]]></category><category><![CDATA[context]]></category><category><![CDATA[github]]></category><category><![CDATA[git]]></category><category><![CDATA[worktree]]></category><category><![CDATA[skills]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Tue, 03 Feb 2026 17:09:04 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2026/02/git-worktrees-hero-3.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2026/02/git-worktrees-hero-3.png" alt="Agentic Coding: Git Worktrees and Agent Skills for Parallel Workflows"><p><strong>From solo agent to parallel powerhouse: how Git worktrees and agent skills turn your agentic coding setup into a multi-tasking machine</strong></p>

<hr>

<p>When you're using agentic coding tools seriously, you've hit this wall:</p>

<ul>
<li>🤖 Agent working on Feature A in one terminal</li>
<li>🤖 Need to start Feature B <em>right now</em></li>
<li>💥 Can't — same working directory, same files, chaos ensues</li>
</ul>

<p>Whether you're running Claude Code in the terminal, using Cursor's Composer, or GitHub Copilot's agent mode in VS Code, the problem is identical: <strong>one working directory, one set of files, one task at a time</strong>.</p>

<p>That's frustrating when you're paying for AI horsepower and want to parallelise.</p>

<p>In this post, I'll show you how <strong>Git worktrees</strong> solve the isolation problem, how <strong>agent skills</strong> teach your AI to manage worktrees autonomously, and how the entire workflow — from branch creation to PR — can be streamlined across multiple parallel sessions.</p>

<blockquote>
  <p>💡 This is <strong>Part 4</strong> in my Context Engineering series. In <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a>, we covered the four pillars of context engineering. In <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a>, we introduced agent skills as portable capabilities. In <a href="https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/">Part 3</a>, we argued why AI-powered teams need lean methodology over Scrum ceremonies. Now we put skills to work.</p>
</blockquote>

<hr>

<h2 id="gitworktreestheparallelsessionssolution">🌳 Git Worktrees: The Parallel Sessions Solution</h2>

<p>Git worktrees let you check out multiple branches from the same repository into <strong>separate directories</strong>. Each worktree has its own working copy with isolated files while sharing the same Git history and <code>.git</code> directory. Unlike cloning the repo multiple times, worktrees are lightweight — they share the Git object store, refs, and hooks, so there's no duplicated history or divergent state to manage.</p>

<pre><code class="language-bash"># Create a new worktree with a new branch
git worktree add ../project-feature-auth -b feature/authentication

# Create a worktree from an existing branch
git worktree add ../project-hotfix-123 hotfix/critical-bug-123

# List all your worktrees
git worktree list

# Remove when done
git worktree remove ../project-feature-auth  
</code></pre>

<p>Why this matters for agentic coding:</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Single Directory</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Git Worktrees</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">One agent at a time</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Multiple agents in parallel</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Context switching = stash/commit/switch</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">No context switching required</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Agents can step on each other's changes</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Complete file isolation</td>
    </tr>
    <tr>
      <td style="padding: 12px;">Wait for task completion</td>
      <td style="padding: 12px;">Work on urgent fix while feature progresses</td>
    </tr>
  </tbody>
</table>

<p>Each worktree is a fresh checkout. Depending on your stack, you'll need to install dependencies — <code>pnpm install</code>, <code>uv sync</code>, <code>bundle install</code>, etc. That's a small price for having three AI agents working simultaneously without stepping on each other.</p>

<p>Claude Code's own documentation <a href="https://code.claude.com/docs/en/common-workflows#run-parallel-claude-code-sessions-with-git-worktrees">recommends exactly this pattern</a> for running parallel sessions. But the docs stop at the manual <code>git worktree</code> commands — what if we could teach the agent to handle worktree management for us?</p>

<hr>

<h2 id="agentskillsteachingyouraigitworktrees">🛠️ Agent Skills: Teaching Your AI Git Worktrees</h2>

<p>In <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a> of this series, I introduced <strong>agent skills</strong> — folders of instructions, scripts, and resources that agents can discover and use. Skills are an emerging convention adopted by Claude Code, Cursor, VS Code, GitHub, Goose, and others. The idea is simple: you drop a skill folder into your project, and the agent automatically discovers it and knows when to use it — no manual configuration required.</p>

<p>Here's the thing: you could teach your agent the <code>git worktree</code> commands every session. Or you could <strong>teach it once</strong> and have it just <em>know</em> how to manage worktrees.</p>

<p>That's exactly what I built: a <a href="https://github.com/shavo007/langchain-anthropic-pdf-support/tree/main/.claude/skills/git-worktree">git-worktree skill</a> that gives Claude Code (or any skill-aware agent) full worktree management capabilities.</p>

<h3 id="theskillstructure">The Skill Structure</h3>

<pre><code>.claude/skills/git-worktree/
├── SKILL.md          # Instructions the agent reads
└── scripts/
    └── worktree.py   # The actual tooling
</code></pre>

<p>The <code>SKILL.md</code> tells the agent what the skill does and — critically — <strong>when to activate</strong>. The <code>description</code> field acts as a trigger: when you ask something that matches, the agent discovers and loads the skill automatically:</p>

<pre><code class="language-yaml">---
name: git-worktree  
description: Git worktree management for parallel development workflows.  
  Use when the user wants to work on multiple branches simultaneously
  without stashing, create isolated environments for features/bugfixes,
  or manage worktrees (create, list, remove, prune). Triggers on
  requests like "create a worktree", "work on branch X in parallel",
  "set up a new feature branch environment".
---
</code></pre>

<p>The Python script handles the heavy lifting — creating worktrees, auto-detecting package managers, installing dependencies, and even opening your preferred editor:</p>

<pre><code class="language-python"># Auto-detects and installs dependencies for any stack
def install_dependencies(path: Path) -&gt; None:  
    if (path / "package-lock.json").exists():
        subprocess.run(["npm", "ci"], cwd=path, check=False)
    elif (path / "pnpm-lock.yaml").exists():
        subprocess.run(["pnpm", "install", "--frozen-lockfile"], cwd=path, check=False)
    elif (path / "uv.lock").exists():
        subprocess.run(["uv", "sync"], cwd=path, check=False)
    elif (path / "go.mod").exists():
        subprocess.run(["go", "mod", "download"], cwd=path, check=False)
    # ... also supports yarn, bun, poetry, pipenv, bundler, cargo
</code></pre>

<h3 id="quickreference">Quick Reference</h3>

<p>These are the commands the agent runs under the hood. You don't need to type them — just describe what you want in natural language and the skill handles the rest.</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Task</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Command</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Create worktree</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;"><code>python scripts/worktree.py create &lt;branch&gt;</code></td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Create + install deps</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;"><code>python scripts/worktree.py create &lt;branch&gt; -i</code></td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Create + open editor</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;"><code>python scripts/worktree.py create &lt;branch&gt; -e code</code></td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">List worktrees</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;"><code>python scripts/worktree.py list</code></td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Remove worktree</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;"><code>python scripts/worktree.py remove &lt;path&gt;</code></td>
    </tr>
    <tr>
      <td style="padding: 12px;">Prune stale refs</td>
      <td style="padding: 12px;"><code>python scripts/worktree.py prune</code></td>
    </tr>
  </tbody>
</table>

<p>Supported editors: VS Code, Cursor, IntelliJ IDEA, WebStorm, PyCharm, Zed, Sublime Text, Vim, Neovim.</p>

<hr>

<h2 id="theskillinaction">⚡ The Skill in Action</h2>

<p>Here's what a parallel agentic workflow looks like with the skill installed.</p>

<h3 id="scenariofeatureworkurgenthotfix">Scenario: Feature work + urgent hotfix</h3>

<p>You're halfway through a feature when a critical bug lands. Instead of stashing or committing half-finished work:</p>

<pre><code class="language-bash"># Terminal 1: You're working on the feature
claude  
&gt; "Create a worktree for fix/critical-bug-123 from main, install deps"

# The agent recognises the request, activates the git-worktree skill,
# and runs the script:
# ✓ Created worktree at ../project-fix/critical-bug-123
# ✓ New branch 'fix/critical-bug-123' from 'main'
# ✓ Running pnpm install --frozen-lockfile...
# ✓ Worktree ready: ../project-fix/critical-bug-123
</code></pre>

<pre><code class="language-bash"># Terminal 2: Open a new Claude Code session in the hotfix worktree
cd ../project-fix/critical-bug-123  
claude  
&gt; "Fix the null pointer in the payment handler"
# Agent works in complete isolation from your feature branch
</code></pre>

<pre><code class="language-bash"># Terminal 1: Meanwhile, your feature work continues uninterrupted
&gt; "Continue implementing the user auth flow"
</code></pre>

<h3 id="scenarioreviewingaprwithoutcontextswitching">Scenario: Reviewing a PR without context switching</h3>

<pre><code class="language-bash">&gt; "Create a worktree from origin/feat/someone-elses-work for code review"

# Agent:
# ✓ Created worktree at ../project-feat/someone-elses-work
# ✓ Using existing branch 'feat/someone-elses-work'
# ✓ Worktree ready
</code></pre>

<p>No stashing. No committing half-finished work. No branch juggling. Each agent gets its own isolated directory, its own dependencies, its own context.</p>

<hr>

<h2 id="thefullloopcommitcommandsplugin">🚀 The Full Loop: Commit Commands Plugin</h2>

<p>Git worktrees handle the <em>beginning</em> of the workflow — creating isolated environments. But what about the <em>end</em>? Committing, pushing, and opening a PR.</p>

<p>This is where Claude Code's <a href="https://github.com/anthropics/claude-code/tree/main/plugins/commit-commands">commit-commands plugin</a> completes the loop.</p>

<p>A quick distinction: <strong>skills</strong> teach the agent domain knowledge (like worktree management) and are activated automatically when relevant, while <strong>plugins</strong> are installable packages that bundle slash commands, hooks, and other extensions. You install plugins from Claude Code's marketplace:</p>

<pre><code class="language-bash"># Browse available plugins
&gt; /plugin

# Or install directly from the official marketplace
&gt; /plugin install commit-commands@claude-plugins-official
</code></pre>

<p>Plugins are namespaced, so the commit-commands plugin provides commands like <code>/commit-commands:commit</code>. For details on the plugin system, see the <a href="https://code.claude.com/docs/en/discover-plugins">Claude Code plugins documentation</a>.</p>

<p>The commit-commands plugin provides three slash commands that work beautifully in tandem with the worktree skill:</p>

<h3 id="commitcommandscommitcontextawarecommits"><code>/commit-commands:commit</code> — Context-Aware Commits</h3>

<pre><code class="language-bash">&gt; /commit-commands:commit

# Claude will:
# - Review staged and unstaged changes
# - Match your repo's commit message style
# - Draft an appropriate message
# - Stage and commit
</code></pre>

<p>The AI reads your recent commit history and generates messages that match your team's conventions. No more generic "fix stuff" commits.</p>

<h3 id="commitcommandscommitpushprbranchtoprinonestep"><code>/commit-commands:commit-push-pr</code> — Branch to PR in One Step</h3>

<p>This is the real power move. After finishing work in your worktree:</p>

<pre><code class="language-bash">&gt; /commit-commands:commit-push-pr

# Claude will:
# - Create a feature branch (if needed)
# - Commit with a meaningful message
# - Push to origin with tracking
# - Create a PR via GitHub CLI with auto-generated description
# - Return the PR URL
</code></pre>

<p>The PR description includes a summary of <em>all</em> commits in the branch (not just the latest), a test plan checklist, and proper attribution.</p>

<h3 id="commitcommandsclean_goneworktreecleanup"><code>/commit-commands:clean_gone</code> — Worktree Cleanup</h3>

<p>After your PRs are merged and remote branches deleted, cleanup is one command:</p>

<pre><code class="language-bash">&gt; /commit-commands:clean_gone

# Claude will:
# - Find branches marked as [gone]
# - Remove associated worktrees
# - Delete stale local branches
# - Report what was cleaned up
</code></pre>

<h3 id="thecompleteworkflow">The Complete Workflow</h3>

<p>Here's the full loop in practice:</p>

<pre><code class="language-bash"># 1. Create isolated worktree (git-worktree skill)
&gt; "Create a worktree for feat/user-auth, install deps, open in VS Code"
# ✓ Worktree ready: ../project-feat/user-auth

# 2. Open a new Claude Code session in the worktree
cd ../project-feat/user-auth  
claude  
&gt; "Implement OAuth2 integration"

# 3. Work is done — commit and create PR (commit-commands plugin)
&gt; /commit-commands:commit-push-pr
# ✓ Committed: Add OAuth2 provider integration
# ✓ Pushed to origin/feat/user-auth
# ✓ PR #142 created: https://github.com/...

# 4. PR is reviewed, approved, and merged on GitHub
# Back in your main worktree, clean up stale branches:
&gt; /commit-commands:clean_gone
# ✓ Removed worktree: ../project-feat/user-auth
# ✓ Deleted branch: feat/user-auth
</code></pre>

<p>From worktree creation to PR to cleanup — streamlined across your parallel sessions.</p>

<hr>

<h2 id="transferabletoothertools">🔄 Transferable to Other Tools</h2>

<p>I'm showcasing this with Claude Code because it's my primary agentic tool, but the concept is <strong>transferable</strong>. The worktree workflow isn't tied to any single tool — it's a pattern you can bring to any agentic coding environment.</p>

<p>If you're using GitHub Copilot's agent mode, you can achieve the same worktree workflow with a <strong>reusable prompt</strong>. Create a file at <code>.github/prompts/git-worktree.prompt.md</code>:</p>

<pre><code class="language-yaml">---
description: Create and manage Git worktrees for parallel development  
tools: ['run_in_terminal', 'get_terminal_output']  
---

# Git Worktree Management

When the user asks to create a worktree, work on a branch in parallel,  
or set up an isolated environment:

1. Determine the branch name and base branch (default: main)  
2. Create a sibling directory: `git worktree add ../repo-name-branch -b &lt;branch&gt;`  
3. Detect and install dependencies (check for package-lock.json, pnpm-lock.yaml,  
   uv.lock, go.mod, etc.)
4. Report the worktree path so the user can open a new agent session there  
</code></pre>

<p>Then invoke it in Copilot's agent mode with <code>/git-worktree</code>.</p>

<p>The key difference is structural: Claude Code skills can bundle executable scripts alongside the instructions (like the <code>worktree.py</code> script), while Copilot reusable prompts rely on the agent generating and running commands from the prompt's instructions alone.</p>

<p>The key insight from <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a> holds: <strong>agent skills and reusable prompts are two implementations of the same idea</strong> — teaching your AI once so you never repeat yourself.</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Tool</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Mechanism</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Location</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Claude Code</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Agent Skills + Plugins</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;"><code>.claude/skills/</code></td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">GitHub Copilot</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Reusable Prompts</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;"><code>.github/prompts/</code></td>
    </tr>
    <tr>
      <td style="padding: 12px;">Cursor</td>
      <td style="padding: 12px;">Rules / Skills</td>
      <td style="padding: 12px;"><code>.cursor/rules/</code></td>
    </tr>
  </tbody>
</table>

<hr>

<h2 id="whythismattersforagenticworkflows">📊 Why This Matters for Agentic Workflows</h2>

<p>The combination of Git worktrees + agent skills + commit commands creates a workflow that's more than the sum of its parts:</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Traditional</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Agentic + Worktrees + Skills</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Work on one task at a time</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Parallel agents on multiple tasks</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Manual branch creation and naming</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">AI-managed with dependency installation</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Write commit messages manually</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Context-aware message generation</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Copy-paste PR descriptions</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Auto-generated from actual changes</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Manual worktree cleanup</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">One-command cleanup of merged branches</td>
    </tr>
    <tr>
      <td style="padding: 12px;">Teach the AI every session</td>
      <td style="padding: 12px;">Teach once via skills, reuse forever</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>💡 <strong>The key insight</strong>: Git worktrees solve the parallelisation problem at the file system level. Agent skills solve the repetitive teaching problem at the cognitive level. Commit commands solve the Git ceremony problem at the workflow level. Together, they let you focus on <strong>directing the AI agents</strong>, not managing the infrastructure around them.</p>
</blockquote>

<hr>

<h2 id="gettingstarted">🎬 Getting Started</h2>

<p><strong>Prerequisites</strong>: <a href="https://code.claude.com/docs/en/quickstart">Claude Code</a> installed and authenticated, Python 3.10+, and <a href="https://cli.github.com/">GitHub CLI</a> (<code>gh</code>) installed and authenticated (needed for PR creation).</p>

<p><strong>1. Add the git-worktree skill to your project</strong></p>

<p>Copy the skill into your project's <code>.claude/skills/</code> directory. You can grab it directly from the repository:</p>

<pre><code class="language-bash"># From your project root
mkdir -p .claude/skills/git-worktree/scripts

# Download the skill files
curl -sL https://raw.githubusercontent.com/shavo007/langchain-anthropic-pdf-support/main/.claude/skills/git-worktree/SKILL.md \  
  -o .claude/skills/git-worktree/SKILL.md

curl -sL https://raw.githubusercontent.com/shavo007/langchain-anthropic-pdf-support/main/.claude/skills/git-worktree/scripts/worktree.py \  
  -o .claude/skills/git-worktree/scripts/worktree.py
</code></pre>

<p>Claude Code automatically discovers skills in <code>.claude/skills/</code> — no configuration needed.</p>

<p>The full skill source is at: <a href="https://github.com/shavo007/langchain-anthropic-pdf-support/tree/main/.claude/skills/git-worktree">github.com/shavo007/langchain-anthropic-pdf-support/.claude/skills/git-worktree</a></p>

<p><strong>2. Install the commit-commands plugin</strong></p>

<p>Install it from Claude Code's official marketplace:</p>

<pre><code class="language-bash">&gt; /plugin install commit-commands@claude-plugins-official
</code></pre>

<p>Once installed, <code>/commit-commands:commit</code>, <code>/commit-commands:commit-push-pr</code>, and <code>/commit-commands:clean_gone</code> are available in any session. See the <a href="https://github.com/anthropics/claude-code/tree/main/plugins/commit-commands">commit-commands README</a> for full details.</p>

<p><strong>3. Create your first parallel worktree</strong></p>

<pre><code class="language-bash">claude  
&gt; "Create a worktree for feat/my-first-parallel-task, install deps"
</code></pre>

<p>The agent will activate the skill, create a sibling directory, set up the branch, and install your project's dependencies. Then open a new terminal, <code>cd</code> into the worktree path the agent reports, and run <code>claude</code> to start a parallel session.</p>

<p><strong>4. For Copilot users</strong></p>

<p>Create <code>.github/prompts/git-worktree.prompt.md</code> in your repository using the reusable prompt example from the Copilot section above. Then invoke it in agent mode with <code>/git-worktree</code>.</p>

<hr>

<h2 id="keytakeaways">🎯 Key Takeaways</h2>

<ol>
<li><strong>Git worktrees</strong> provide complete file isolation for running multiple agentic sessions in parallel  </li>
<li><strong>Agent skills</strong> teach your AI worktree management once — no more repeating instructions  </li>
<li><strong>The concept is transferable</strong> across tools: Claude Code skills, Copilot reusable prompts, Cursor rules  </li>
<li><strong>Plugins like commit-commands</strong> complete the loop: from worktree creation through PR and cleanup  </li>
<li><strong>Teach once, reuse forever</strong> — the core principle of context engineering applied to your Git workflow</li>
</ol>

<hr>

<h2 id="resources">🔗 Resources</h2>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🌳 <a href="https://github.com/shavo007/langchain-anthropic-pdf-support/tree/main/.claude/skills/git-worktree"><strong>Git Worktree Skill</strong></a> — The skill showcased in this post</li>
  <li style="padding: 8px 0;">🚀 <a href="https://github.com/anthropics/claude-code/tree/main/plugins/commit-commands"><strong>Commit Commands Plugin</strong></a> — Claude Code's commit, push, and PR commands</li>
  <li style="padding: 8px 0;">💻 <a href="https://code.claude.com/docs/en/common-workflows#run-parallel-claude-code-sessions-with-git-worktrees"><strong>Claude Code: Parallel Sessions with Git Worktrees</strong></a> — Official Claude Code docs on worktree workflows</li>
  <li style="padding: 8px 0;">📖 <a href="https://git-scm.com/docs/git-worktree"><strong>Git Worktree Documentation</strong></a> — Official Git docs</li>
  <li style="padding: 8px 0;">📝 <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/"><strong>Context Engineering Part 1</strong></a> — The four pillars</li>
  <li style="padding: 8px 0;">🛠️ <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/"><strong>Context Engineering Part 2</strong></a> — Agent skills and the Agentic AI Foundation</li>
  <li style="padding: 8px 0;">⚡ <a href="https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/"><strong>Context Engineering Part 3</strong></a> — Scrum is Dead: why AI-powered teams need lean methodology</li>
</ul>]]></content:encoded></item><item><title><![CDATA[Scrum is Dead]]></title><description><![CDATA[<p><strong>Why AI-powered teams need lean methodology, not ceremonies</strong></p>

<hr>

<p>Let me ask you something.</p>

<p>How many hours did you spend in meetings this week? Sprint planning. Daily standups. Backlog grooming. Sprint review. Retro.</p>

<p>Now ask yourself: did any of those meetings help you ship faster?</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🗓️ Are your sprints constantly disrupted by</li></ul>]]></description><link>https://blog.shanelee.name/2026/01/30/scrum-is-dead-why-ai-powered-teams-need-lean-methodology-not-ceremonies/</link><guid isPermaLink="false">bf476f36-0aea-48be-a4ac-69ebb37346c3</guid><category><![CDATA[ai]]></category><category><![CDATA[agile]]></category><category><![CDATA[claude]]></category><category><![CDATA[github]]></category><category><![CDATA[agentic]]></category><category><![CDATA[accelerate]]></category><category><![CDATA[leadership]]></category><category><![CDATA[scrum]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Fri, 30 Jan 2026 14:45:48 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2026/01/scrum-is-dead-hero-3.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2026/01/scrum-is-dead-hero-3.png" alt="Scrum is Dead"><p><strong>Why AI-powered teams need lean methodology, not ceremonies</strong></p>

<hr>

<p>Let me ask you something.</p>

<p>How many hours did you spend in meetings this week? Sprint planning. Daily standups. Backlog grooming. Sprint review. Retro.</p>

<p>Now ask yourself: did any of those meetings help you ship faster?</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🗓️ Are your sprints constantly disrupted by changing priorities?</li>
  <li style="padding: 8px 0;">📝 Do you spend more time writing Jira tickets than writing code?</li>
  <li style="padding: 8px 0;">🔄 Does "Agile" feel anything but agile?</li>
</ul>

<p>In <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a> and <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a> of this series, I showed how Context Engineering—giving AI the right information at the right time—can cut project costs by 98%. But there's a problem: <strong>your process can't keep up.</strong></p>

<p>When a Spring Boot migration takes 15 minutes instead of 2-3 days, when AI agents can execute complex workflows autonomously—the 2-week sprint starts to feel... quaint.</p>

<p>Here's the uncomfortable truth:</p>

<blockquote>
  <p>💡 <strong>Scrum has become the very thing the Agile Manifesto rebelled against.</strong></p>
</blockquote>

<p>In Parts 1 and 2, we covered the <em>technical</em> side of Context Engineering—AGENTS.md, MCPs, Custom Agents, and Prompts. But here's what I didn't address: <strong>what happens to your team's process when AI can complete a sprint's worth of work in an afternoon?</strong></p>

<hr>

<h2 id="whattheagilemanifestoactuallysaid">🎯 What the Agile Manifesto Actually Said</h2>

<p>Let's go back to 2001. The Agile Manifesto was a rebellion against heavyweight, document-driven development. It valued:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">👥 <strong>Individuals and interactions</strong> over processes and tools</li>
  <li style="padding: 8px 0;">💻 <strong>Working software</strong> over comprehensive documentation</li>
  <li style="padding: 8px 0;">🤝 <strong>Customer collaboration</strong> over contract negotiation</li>
  <li style="padding: 8px 0;">🔄 <strong>Responding to change</strong> over following a plan</li>
</ul>

<p>Notice what's <strong>NOT</strong> in the Manifesto:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">❌ Two-week sprints</li>
  <li style="padding: 8px 0;">❌ Story points</li>
  <li style="padding: 8px 0;">❌ Jira</li>
  <li style="padding: 8px 0;">❌ Daily standups</li>
  <li style="padding: 8px 0;">❌ Sprint ceremonies</li>
  <li style="padding: 8px 0;">❌ Velocity tracking</li>
  <li style="padding: 8px 0;">❌ Backlog grooming</li>
</ul>

<p>The Manifesto was about <strong>principles</strong>, not <strong>processes</strong>. Scrum added the process layer—and over two decades, that layer calcified into the very bureaucracy Agile sought to escape.</p>

<blockquote>
  <p>📜 <strong>We replaced waterfall documentation with Jira stories. We replaced status meetings with daily standups. We swapped one bureaucracy for another.</strong></p>
</blockquote>

<hr>

<h2 id="whyaibreaksscrum">⚡ Why AI Breaks Scrum</h2>

<p>The fundamental assumption of Scrum is that software development happens at a predictable, human pace. Sprint planning assumes you can estimate work. Velocity tracking assumes consistency. Two-week iterations assume that's a reasonable feedback loop.</p>

<p>AI development breaks all of these assumptions.</p>

<h3 id="thespeedmismatch">The Speed Mismatch</h3>

<p>In my Context Engineering series (<a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a> and <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Part 2</a>), I documented real results:</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Task</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Traditional</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">With AI</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Spring Boot 4.0 migration</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">2-3 days per app</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">15 minutes per app</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Business case document</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">2-3 days</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">4 hours</td>
    </tr>
    <tr>
      <td style="padding: 12px;">10 app migration project</td>
      <td style="padding: 12px;">$20,000</td>
      <td style="padding: 12px;">$350 (98% reduction)</td>
    </tr>
  </tbody>
</table>

<p>When tasks complete in minutes instead of days:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📅 Sprint planning becomes obsolete before the sprint ends</li>
  <li style="padding: 8px 0;">🎯 Estimates are meaningless when AI can complete tasks 10x faster than expected</li>
  <li style="padding: 8px 0;">📋 The backlog churns faster than you can groom it</li>
</ul>

<p>Paul Dix calls this <a href="https://www.linkedin.com/pulse/2026-great-engineering-divergence-paul-dix-9qy0e/">The Great Engineering Divergence</a>. He applies Amdahl's Law—a principle from computer performance theory—to software delivery:</p>

<blockquote>
  <p>📐 <strong>"If coding is 20% of the end-to-end cycle, making it 10x faster only yields ~1.25x overall speedup. Once coding speed jumps, everything around it becomes the constraint."</strong></p>
</blockquote>

<p>Developers report 20-50% productivity gains from AI, but Dix argues they could achieve <strong>10x, 100x or more</strong>—if their pipelines could handle it. The bottlenecks aren't in the code. They're in code review bandwidth, testing capacity, release confidence, security gates, and <strong>sprint ceremonies</strong>.</p>

<p>In other words: <strong>Scrum is the constraint.</strong></p>

<hr>

<h2 id="whattheindustryleadersaresaying">🗣️ What the Industry Leaders Are Saying</h2>

<p>I'm not alone in this observation. The developers building with AI every day are reaching the same conclusion.</p>

<h3 id="geoffhuntleycreatorofralphwiggumtechnique">Geoff Huntley (Creator of Ralph Wiggum Technique)</h3>

<blockquote>
  <p><strong>"Agile and standups doesn't make sense any more."</strong></p>
  
  <p><strong>"The days of being a Jira ticket monkey are over."</strong></p>
</blockquote>

<p>Huntley's <a href="https://www.theregister.com/2026/01/27/ralph_wiggum_claude_loops/">Ralph Wiggum technique</a> runs AI in continuous loops:</p>

<pre><code class="language-bash">while :; do cat PROMPT.md | claude-code ; done  
</code></pre>

<p>This deceptively simple script feeds a prompt file into Claude Code repeatedly—the AI attempts a task, evaluates its own errors, and retries until it succeeds. At ~$10/hour in compute costs, this technique has cloned Atlassian products and created entire programming languages. Humans stay "in the loop" but enter <strong>later and less frequently</strong> than traditional workflows demand.</p>

<hr>

<h3 id="petersteinbergerpspdfkitfounder">Peter Steinberger (PSPDFKit Founder)</h3>

<p>From his <a href="https://newsletter.pragmaticengineer.com/p/the-creator-of-clawd-i-ship-code">Pragmatic Engineer interview</a>:</p>

<blockquote>
  <p><strong>"Pull requests are dead, long live 'prompt requests.'"</strong></p>
  
  <p><strong>"Code reviews are dead for this workflow—architecture discussions replace them."</strong></p>
  
  <p><strong>"I ship code I don't read."</strong></p>
</blockquote>

<p>Steinberger runs 5-10 AI agents simultaneously, maintains flow state throughout development, and deliberately rejects remote CI in favour of local validation. Why wait 10 minutes for a pipeline when agents can validate in seconds?</p>

<hr>

<h2 id="theleanalternative">🔄 The Lean Alternative</h2>

<p>So what replaces Scrum? Return to the Manifesto's principles—but with AI-native practices. If you've been following this series, you'll recognise Agentic Context Engineering at work here.</p>

<h3 id="fromceremoniestocontinuousflowagenticcontextengineeringinaction">From Ceremonies to Continuous Flow (Agentic Context Engineering in Action)</h3>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Scrum Practice</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">AI-Native Alternative</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Sprint planning</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Just pull the next card — work completes too fast to batch-plan</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Daily standups</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Real-time observability — PRs, commits, and deploys speak for themselves</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Jira stories</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Specs define intent, Plan mode designs approach, AGENTS.md provides context</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Story point estimation</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Just run it and see (<em>inference-speed feedback</em>)</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Code reviews</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Focus shifts — less syntax, more architecture, security, and prompt review</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Remote CI (10+ min)</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Local validation in seconds — agents run CI via Skills or AGENTS.md</td>
    </tr>
    <tr>
      <td style="padding: 12px;">2-week sprints</td>
      <td style="padding: 12px;">Continuous delivery</td>
    </tr>
  </tbody>
</table>

<hr>

<h2 id="agentsmdprojectcontextforai">📋 AGENTS.md: Project Context for AI</h2>

<p>In my <a href="https://github.com/shavo007/langchain-anthropic-pdf-support">langchain-anthropic-pdf-support</a> project, I use AGENTS.md to document everything an AI agent needs to work on the codebase.</p>

<p><strong>What AGENTS.md replaces:</strong></p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📚 Confluence documentation</li>
  <li style="padding: 8px 0;">🎓 Onboarding wikis</li>
  <li style="padding: 8px 0;">🧠 Tribal knowledge</li>
  <li style="padding: 8px 0;">📝 Acceptance criteria patterns (the reusable parts)</li>
</ul>

<p><strong>What AGENTS.md doesn't replace:</strong> You still need to capture the task itself—what you're working on right now. That might be a Kanban card, a GitHub issue, or a prompt to the agent. Tools like <a href="https://openspec.dev/">OpenSpec</a> take this further with spec-driven development—you write specs before code, and AI agents read them to understand intent. The difference is that task definitions become lightweight because all the project context lives in AGENTS.md.</p>

<p><strong>Example structure:</strong></p>

<pre><code class="language-markdown">## Development Environment
- Run agent: `uv run poe dev`
- FastAPI server: `uv run poe serve`

## Testing Strategy
- Unit tests: `uv run poe test`
- Full CI locally: `uv run poe ci`

## Key Architecture Decisions
- Default model and why
- Caching strategy
- API design patterns

## Code Patterns
- How to add new tools
- Testing conventions

## Important Gotchas
- API key requirements
- Coverage targets (80%)
</code></pre>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Scattered Documentation</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">AGENTS.md</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Confluence pages with prose</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Executable commands</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Lives in external tools</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Lives in the repo, versioned with code</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Gets stale, disconnected</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Updated with every PR</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Requires human interpretation</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Machine-readable</td>
    </tr>
    <tr>
      <td style="padding: 12px;">Tribal knowledge in people's heads</td>
      <td style="padding: 12px;">Codified patterns and gotchas</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>💡 <strong>An AI agent can read AGENTS.md and immediately know how to run, test, and contribute to the project. Give it a task—via a Kanban card, GitHub issue, or prompt—and it has all the context it needs.</strong></p>
</blockquote>

<hr>

<h2 id="localcishiftlefttoseconds">⚡ Local CI: Shift Left to Seconds</h2>

<p>Why wait for remote CI when you can validate locally in seconds?</p>

<p>In my projects, I use <a href="https://github.com/shavo007/langchain-anthropic-pdf-support?tab=readme-ov-file#pre-commit-hooks">pre-commit hooks</a> with 9 automated checks:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🔍 <strong>ruff</strong> — Linting and formatting</li>
  <li style="padding: 8px 0;">🔒 <strong>detect-private-key</strong> — Security check</li>
  <li style="padding: 8px 0;">📝 <strong>mypy</strong> — Strict type checking</li>
  <li style="padding: 8px 0;">✅ <strong>check-yaml, check-merge-conflict</strong> — File hygiene</li>
</ul>

<p><strong>The workflow comparison:</strong></p>

<pre><code>Traditional:  
Code → Commit → Push → Wait 10min for CI → Fix → Repeat

AI-native:  
Code → Agent validates locally → Commit → Push → CI passes first time  
</code></pre>

<p>The command <code>uv run poe ci</code> mirrors exactly what remote CI would do, but runs locally in seconds. Combined with pre-commit hooks, problematic code never leaves your machine.</p>

<p>There are two ways to achieve this with Claude Code:</p>

<p><strong>Approach 1: Hooks.</strong> Configure hooks to automatically run linting and formatting after every file change. The agent validates its own work in real-time, catching issues before you even see them.</p>

<p><strong>Approach 2: Document commands in AGENTS.md.</strong> Tell the agent what validation commands exist so it knows how to verify its changes:</p>

<pre><code class="language-markdown">## Validation
- After editing files: `uv run poe check` (lint + format + typecheck)
- Before committing: `uv run poe ci` (full validation suite)
</code></pre>

<p>The agent reads this, runs the checks, and self-corrects.</p>

<hr>

<h2 id="makingthetransition">🎬 Making the Transition</h2>

<p>You don't have to abandon everything overnight. Start here:</p>

<h3 id="week1addagentsmd">Week 1: Add AGENTS.md</h3>

<p>Document your project's development workflow, testing strategy, and architecture decisions. Make it executable—commands, not prose.</p>

<h3 id="week2setuplocalvalidation">Week 2: Set Up Local Validation</h3>

<p>Configure pre-commit hooks. Add a <code>poe ci</code> or equivalent command that mirrors your remote pipeline. Stop waiting for GitHub Actions.</p>

<h3 id="week3questioneachceremony">Week 3: Question Each Ceremony</h3>

<p>For every meeting on your calendar, ask: "Does this help us ship faster?" If the answer is "we've always done it this way," that's not a reason.</p>

<h3 id="week4measure">Week 4: Measure</h3>

<p>Track time spent in ceremonies vs. time shipping. The numbers will speak for themselves.</p>

<hr>

<h2 id="keytakeaways">🎯 Key Takeaways</h2>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">1️⃣ <strong>The Agile Manifesto never prescribed Scrum</strong> — ceremonies were added later</li>
  <li style="padding: 8px 0;">2️⃣ <strong>AI development happens at inference speed</strong> — 2-week sprints can't keep up</li>
  <li style="padding: 8px 0;">3️⃣ <strong>AGENTS.md replaces scattered documentation</strong> — project context lives in the repo, not Confluence</li>
  <li style="padding: 8px 0;">4️⃣ <strong>Local CI replaces remote waiting</strong> — validate in seconds, not minutes</li>
  <li style="padding: 8px 0;">5️⃣ <strong>Architecture discussions replace code reviews</strong> — review prompts, not generated code</li>
  <li style="padding: 8px 0;">6️⃣ <strong>Context Engineering gave you the tools</strong> — now remove the constraints that slow them down</li>
</ul>

<blockquote>
  <p>✨ <strong>The days of being a Jira ticket monkey are over. The question is: will you adapt—reviewing architecture instead of syntax, shipping continuously instead of in sprints—or will you keep attending standups while the industry moves on?</strong></p>
</blockquote>

<hr>

<h2 id="resources">🔗 Resources</h2>

<p><strong>This Series:</strong></p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📖 <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Context Engineering Part 1</a> — The four pillars</li>
  <li style="padding: 8px 0;">📖 <a href="https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/">Context Engineering Part 2</a> — AAIF and real examples</li>
</ul>

<p><strong>References:</strong></p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📐 <a href="https://www.linkedin.com/pulse/2026-great-engineering-divergence-paul-dix-9qy0e/">Paul Dix: The Great Engineering Divergence</a> — Amdahl's Law applied to delivery</li>
  <li style="padding: 8px 0;">📰 <a href="https://newsletter.pragmaticengineer.com/p/the-creator-of-clawd-i-ship-code">Pragmatic Engineer: I Ship Code I Don't Read</a></li>
  <li style="padding: 8px 0;">📰 <a href="https://www.theregister.com/2026/01/27/ralph_wiggum_claude_loops/">The Register: Ralph Wiggum Technique</a></li>
  <li style="padding: 8px 0;">📜 <a href="https://agilemanifesto.org/">The Agile Manifesto</a> — What it actually says</li>
  <li style="padding: 8px 0;">🐙 <a href="https://github.com/shavo007/langchain-anthropic-pdf-support">langchain-anthropic-pdf-support</a> — AGENTS.md and pre-commit example</li>
</ul>]]></content:encoded></item><item><title><![CDATA[Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt2]]></title><description><![CDATA[<p><strong>From Theory to Practice: How the industry is converging on context engineering standards</strong></p>

<hr>

<p>In <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a>, we explored the fundamentals of context engineering—the four pillars, the hybrid model, and why teaching AI once beats fighting it every time.</p>

<p>Now let's get practical. In this post, I'll show you:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🏛️ How</li></ul>]]></description><link>https://blog.shanelee.name/2026/01/26/context-engineering-stop-fighting-your-ai-and-start-shipping-faster-pt2/</link><guid isPermaLink="false">a1534f8b-650d-4928-86e2-dbcab2a19dec</guid><category><![CDATA[ai]]></category><category><![CDATA[copilot]]></category><category><![CDATA[context]]></category><category><![CDATA[agentic]]></category><category><![CDATA[claude]]></category><category><![CDATA[github]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Mon, 26 Jan 2026 17:47:19 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2026/01/context-engineering-part2-hero-3.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2026/01/context-engineering-part2-hero-3.png" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt2"><p><strong>From Theory to Practice: How the industry is converging on context engineering standards</strong></p>

<hr>

<p>In <a href="https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/">Part 1</a>, we explored the fundamentals of context engineering—the four pillars, the hybrid model, and why teaching AI once beats fighting it every time.</p>

<p>Now let's get practical. In this post, I'll show you:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🏛️ How the <strong>Agentic AI Foundation</strong> validates this approach</li>
  <li style="padding: 8px 0;">📋 Why <strong>AGENTS.md</strong> is becoming the new standard</li>
  <li style="padding: 8px 0;">🚀 A real case study: <strong>98% cost reduction</strong> on Spring Boot 4.0 migrations</li>
</ul>

<hr>

<h2 id="theagenticaifoundationindustryconvergence">🏛️ The Agentic AI Foundation: Industry Convergence</h2>

<p>Something significant happened in December 2025. The <strong>Agentic AI Foundation</strong> was announced—a directed fund under the Linux Foundation ensuring agentic AI evolves transparently and collaboratively.</p>

<p>The founding members might surprise you:</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Company</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Contribution</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Anthropic</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Model Context Protocol (MCP)</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">OpenAI</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">AGENTS.md specification</td>
    </tr>
    <tr>
      <td style="padding: 12px;">Block</td>
      <td style="padding: 12px;">Goose autonomous agent</td>
    </tr>
  </tbody>
</table>

<p>Supporting members include <strong>Google</strong>, <strong>Microsoft</strong>, <strong>AWS</strong>, <strong>Cloudflare</strong>, and <strong>Bloomberg</strong>.</p>

<blockquote>
  <p>💡 <strong>The key insight:</strong> The industry is converging on the same patterns I covered in Part 1. AAIF formalises what forward-thinking teams have already been doing.</p>
</blockquote>

<hr>

<h2 id="thethreecorecontributions">📋 The Three Core Contributions</h2>

<h3 id="agentsmdfromopenai">AGENTS.md (from OpenAI)</h3>

<p>A standardised format for AI agent instructions—think of it as a <strong>"README for AI agents"</strong>.</p>

<pre><code class="language-markdown"># AGENTS.md

## Dev environment tips
- Use `pnpm install` for dependencies
- Run `pnpm dev` for local development

## Testing instructions
- Run `pnpm test` before committing
- Ensure 80% coverage minimum

## PR instructions
- Title format: [JIRA-123] Description
- Always run linting before push
</code></pre>

<p>This maps directly to the <strong>Instructions pillar</strong> from Part 1. The difference? Now it's an open standard adopted across the industry.</p>

<hr>

<h3 id="mcpfromanthropic">MCP (from Anthropic)</h3>

<p>The Model Context Protocol is the <strong>"USB-C for AI tools"</strong>—an open standard for connecting AI to external systems.</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📊 <strong>10,000+</strong> active MCP servers</li>
  <li style="padding: 8px 0;">📦 <strong>97M+</strong> monthly SDK downloads</li>
  <li style="padding: 8px 0;">🔧 Adopted by VS Code, Cursor, ChatGPT, Gemini</li>
</ul>

<p>This is the <strong>MCPs pillar</strong> formalised. Instead of copying documentation into prompts (which gets stale), MCP gives AI direct access to authoritative, always-current sources.</p>

<hr>

<h3 id="goosefromblock">Goose (from Block)</h3>

<p>An open-source autonomous AI agent that <strong>"actually does the work"</strong>.</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🏗️ Builds entire projects</li>
  <li style="padding: 8px 0;">🐛 Executes and debugs code</li>
  <li style="padding: 8px 0;">🔌 Native MCP integration</li>
  <li style="padding: 8px 0;">⭐ <strong>24.8k+</strong> GitHub stars</li>
</ul>

<p>This maps to the <strong>Custom Agents pillar</strong>—role-based AI behaviour for different tasks.</p>

<hr>

<h2 id="mappingaaiftothefourpillars">🗺️ Mapping AAIF to the Four Pillars</h2>

<p>Here's how the Agentic AI Foundation contributions extend the framework from Part 1:</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Pillar</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Implementation</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">AAIF Contribution</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">📜 Instructions</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">.github/instructions/*.md</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">AGENTS.md - Standardised format</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">🎭 Custom Agents</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">.github/agents/*.agent.md</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Goose profiles & extensions</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">📝 Prompts</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">.github/prompts/*.md</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">AGENTS.md task guidance</td>
    </tr>
    <tr>
      <td style="padding: 12px;">🔌 MCPs</td>
      <td style="padding: 12px;">mcp.json configuration</td>
      <td style="padding: 12px;">MCP - Official protocol</td>
    </tr>
  </tbody>
</table>

<hr>

<h2 id="agentskillsportablecapabilities">🛠️ Agent Skills: Portable Capabilities</h2>

<p>In December 2025, Anthropic published <strong>Agent Skills</strong> as an open standard—folders of instructions, scripts, and resources that agents can discover and use.</p>

<pre><code class="language-yaml">---
name: spring-boot-migration  
description: Migrates Spring Boot 3.x apps  
  to 4.0 with dependency modularisation,
  Jackson 3 updates, and Jakarta EE 11.
license: Apache-2.0  
metadata:  
  author: your-name
  version: "1.0"
---

## Instructions
1. Analyze current dependencies...  
2. Update starter modules...

## References
See [migration guide](references/GUIDE.md)  
</code></pre>

<p>Skills enable:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🧠 <strong>Domain Expertise</strong> - Package specialized knowledge into reusable instructions</li>
  <li style="padding: 8px 0;">🔄 <strong>Interoperability</strong> - Reuse the same skill across different agent products</li>
  <li style="padding: 8px 0;">⚡ <strong>New Capabilities</strong> - Give agents abilities like creating presentations or analysing datasets</li>
  <li style="padding: 8px 0;">✅ <strong>Repeatable Workflows</strong> - Turn multi-step tasks into consistent, auditable workflows</li>
</ul>

<p>Adopted by: <strong>Cursor</strong>, <strong>VS Code</strong>, <strong>GitHub</strong>, <strong>Claude Code</strong>, <strong>Goose</strong>, <strong>OpenAI Codex</strong>, and <strong>Factory</strong>.</p>

<hr>

<h2 id="casestudyspringboot40migration">📖 Case Study: Spring Boot 4.0 Migration</h2>

<p>Let me show you context engineering in action with a real migration project.</p>

<p><img src="https://blog.shanelee.name/content/images/2026/01/migration-approach-comparison-1.svg" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt2"></p>

<h3 id="themetainstructionpattern">The Meta-Instruction Pattern</h3>

<p>I used a <strong>meta-instruction pattern</strong>—having AI generate the migration instructions, then use those same instructions to execute the migration.</p>

<p>The secret weapon? The <a href="https://github.com/github/awesome-copilot/blob/main/instructions/instructions.instructions.md">instructions.instructions.md</a> file from the <strong>awesome-copilot</strong> repository—a meta-instruction that teaches AI how to write high-quality instructions.</p>

<p><strong>Phase 1: Generate Migration Instructions (5 minutes)</strong></p>

<pre><code>Using #file:instructions.instructions.md  
and examples like  
#file:java-21-to-java-25-upgrade.instructions.md

I want to define new instructions for  
Spring Boot 4 migration guide.

You can find the guide here:  
https://github.com/spring-projects/  
spring-boot/wiki/  
Spring-Boot-4.0-Migration-Guide

User Feedback:  
"Can focus on gradle and kotlin
as that is what we mostly use"  
</code></pre>

<p>What the AI does: <br>
1. Fetches official Spring Boot 4.0 guide <br>
2. Analyzes existing instruction patterns <br>
3. Extracts key migration themes <br>
4. Incorporates project preferences (Gradle/Kotlin) <br>
5. Generates <code>springboot-4-migration.instructions.md</code></p>

<p><strong>Result:</strong> 1,400+ line comprehensive migration guide created in 3 minutes.</p>

<hr>

<p><strong>Phase 2: Execute Migration (10 minutes)</strong></p>

<pre><code>Migrate this project to Spring Boot 4.0  
using the generated instructions in  
#file:springboot-4-migration.instructions.md
</code></pre>

<p>What Plan Agent Mode does:</p>

<pre><code>✅ Read migration instructions
✅ Analyze current pom.xml
✅ Update dependencies:
   spring-boot-starter-web →
   spring-boot-starter-webmvc
✅ Add spring-boot-starter-webmvc-test
✅ Verify Jackson property paths
✅ Run: ./mvnw clean compile
✅ BUILD SUCCESS
</code></pre>

<blockquote>
  <p>🎯 <strong>The key innovation:</strong> AI generates the migration guide... then AI uses that guide to execute the migration.</p>
</blockquote>

<hr>

<h3 id="givingbacktoopensource">Giving Back to Open Source</h3>

<p><img src="https://blog.shanelee.name/content/images/2026/01/open-source-contribution-banner-1.svg" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt2"></p>

<p>The Spring Boot 4.0 migration instructions I generated have been <strong>contributed back to the community</strong>. You can use them right now in your own projects—no need to recreate the wheel.</p>

<hr>

<h3 id="theroi98costreduction">The ROI: 98% Cost Reduction</h3>

<p><img src="https://blog.shanelee.name/content/images/2026/01/roi-cost-reduction.svg" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt2"></p>

<p>The numbers speak for themselves. What used to cost $20,000 across 10 applications now costs $350—and you get better quality with zero errors.</p>

<hr>

<h2 id="thedevelopersroleevolves">👨‍💻 The Developer's Role Evolves</h2>

<p>This isn't about replacing developers. It's about changing what we do.</p>

<p><img src="https://blog.shanelee.name/content/images/2026/01/developer-role-evolution.svg" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt2"></p>

<p>The shift is clear: fewer repetitive tasks, more strategic thinking. AI handles the implementation grind while you focus on what matters—architecture, requirements, and business value.</p>

<hr>

<h2 id="validationfromthesource">🎓 Validation from the Source</h2>

<p>Here's something that caught my attention. Boris Cherny, the creator of Claude Code at Anthropic, shared his actual workflow on X:</p>

<p><img src="https://blog.shanelee.name/content/images/2026/01/boris-cherny-tweet-1.png" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt2" there="" is="" no="" one="" correct="" way="" to="" use="" claude="" code:="" we="" intentionally="" build="" it="" in="" a="" that="" you="" can="" it,="" customize="" and="" hack="" however="" like.""=""></p>

<p>What's fascinating is how his practices map directly to the context engineering pillars:</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">His Practice</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Context Engineering</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">CLAUDE.md</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">AGENTS.md</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Subagents</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Chatmodes / Agents</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Slash commands</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Prompts</td>
    </tr>
    <tr>
      <td style="padding: 12px;">Verification loops</td>
      <td style="padding: 12px;">Shift-left testing</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>💡 <strong>"Every mistake becomes a rule."</strong> — Boris Cherny</p>
  
  <p>💰 On using Opus (the best model): <strong>"Paying the 'compute tax' upfront eliminates the 'correction tax' later."</strong></p>
</blockquote>

<p>The exact same patterns work in production at one of the world's leading AI companies. Independent validation that this approach works at scale.</p>

<hr>

<h2 id="gettingstarted">🚀 Getting Started</h2>

<h3 id="essentialmcpconfiguration">Essential MCP Configuration</h3>

<p>Here's a practical MCP setup for local development:</p>

<pre><code class="language-json">{
  "servers": {
    "atlassian/atlassian-mcp-server": {
      "type": "stdio",
      "command": "pnpm",
      "args": ["dlx", "mcp-remote",
        "https://mcp.atlassian.com/v1/sse"]
    },
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp/"
    },
    "context7": {
      "type": "stdio",
      "command": "pnpm",
      "args": ["dlx", "@upstash/context7-mcp@latest"]
    }
  }
}
</code></pre>

<p><strong>What this enables:</strong></p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📋 <strong>Atlassian MCP</strong> - Query Jira issues, search Confluence, create tickets from chat</li>
  <li style="padding: 8px 0;">🐙 <strong>GitHub MCP</strong> - Search repositories, read files, create pull requests</li>
  <li style="padding: 8px 0;">📚 <strong>Context7 MCP</strong> - Fetch latest library documentation, always up-to-date API references</li>
</ul>

<hr>

<h3 id="theawesomecopilotrepository">The Awesome Co-Pilot Repository</h3>

<p>The <a href="https://github.com/github/awesome-copilot">awesome-copilot</a> repository is <mark>a goldmine for context engineering</mark>. It's a community-driven collection of <strong>163+ instruction files</strong> covering virtually every technology stack you might work with.</p>

<p><strong>What's inside:</strong></p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">💻 <strong>Programming languages</strong> - Python, Java, TypeScript, Go, Rust, and more</li>
  <li style="padding: 8px 0;">🔧 <strong>Frameworks</strong> - React, Vue, Spring Boot, Django, FastAPI</li>
  <li style="padding: 8px 0;">☁️ <strong>Infrastructure</strong> - Terraform, Docker, Kubernetes, GitHub Actions</li>
  <li style="padding: 8px 0;">🔒 <strong>Security</strong> - OWASP standards, secure coding practices</li>
</ul>

<p><strong>The meta-instruction magic:</strong></p>

<p>The real power comes from <a href="https://github.com/github/awesome-copilot/blob/main/instructions/instructions.instructions.md">instructions.instructions.md</a>—<mark>a meta-instruction that teaches AI how to write high-quality instructions</mark>. It defines:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">📋 <strong>Required frontmatter structure</strong> - Description and file pattern matching</li>
  <li style="padding: 8px 0;">📝 <strong>Organized content sections</strong> - Guidelines, best practices, code standards</li>
  <li style="padding: 8px 0;">✅ <strong>Concrete examples</strong> - Both recommended and discouraged approaches</li>
  <li style="padding: 8px 0;">🧪 <strong>Validation requirements</strong> - Build commands, linting, testing steps</li>
</ul>

<p><mark>This is the bootstrap for context engineering</mark>—use it to generate domain-specific instructions for your own projects, then use those instructions to guide AI through complex tasks.</p>

<hr>

<h2 id="resources">🔗 Resources</h2>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🛠️ <strong>awesome-copilot MCP Server</strong> - <a href="https://github.com/github/awesome-copilot">github.com/github/awesome-copilot</a></li>
  <li style="padding: 8px 0;">🍃 <strong>Agent Skills Specification</strong> - <a href="https://github.com/anthropics/agent-skills">github.com/anthropics/agent-skills</a></li>
  <li style="padding: 8px 0;">📖 <strong>Official Spring Boot 4.0 Migration Guide</strong> - <a href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide">Spring Boot Wiki</a></li>
  <li style="padding: 8px 0;">🏛️ <strong>Agentic AI Foundation</strong> - <a href="https://agenticaifoundation.org">agenticaifoundation.org</a></li>
</ul>

<hr>

<h2 id="keytakeaways">🎯 Key Takeaways</h2>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">1️⃣ <strong>AAIF validates the approach</strong> - The industry is standardising on context engineering</li>
  <li style="padding: 8px 0;">2️⃣ <strong>AGENTS.md standardises instructions</strong> - One format, every agent</li>
  <li style="padding: 8px 0;">3️⃣ <strong>Meta-instructions accelerate adoption</strong> - Have AI generate the guides, then use them</li>
  <li style="padding: 8px 0;">4️⃣ <strong>98% cost reduction is achievable</strong> - This isn't theoretical, it's measured</li>
</ul>

<blockquote>
  <p>✨ <strong>The future isn't about better prompts. It's about better context.</strong></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt1]]></title><description><![CDATA[<p><strong>How I went from repeating instructions every conversation to having AI that actually understands my codebase</strong></p>

<hr>

<p>Let me ask you a few questions:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">😤 Are you frustrated with AI giving inconsistent results?</li>
  <li style="padding: 8px 0;">🔁 Do you find yourself repeating the same instructions over and over?</li>
  <li style="padding: 8px 0;">🤔 Do you wish AI just <em>knew</em> your project</li></ul>]]></description><link>https://blog.shanelee.name/2026/01/25/context-engineering-stop-fighting-your-ai-and-start-shipping-faster/</link><guid isPermaLink="false">c58e4df0-a274-429f-a9de-98057cbe0f77</guid><category><![CDATA[ai]]></category><category><![CDATA[copilot]]></category><category><![CDATA[agentic]]></category><category><![CDATA[claude]]></category><category><![CDATA[context]]></category><category><![CDATA[mcp]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Sun, 25 Jan 2026 13:17:23 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2026/01/hero-image.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2026/01/hero-image.png" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt1"><p><strong>How I went from repeating instructions every conversation to having AI that actually understands my codebase</strong></p>

<hr>

<p>Let me ask you a few questions:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">😤 Are you frustrated with AI giving inconsistent results?</li>
  <li style="padding: 8px 0;">🔁 Do you find yourself repeating the same instructions over and over?</li>
  <li style="padding: 8px 0;">🤔 Do you wish AI just <em>knew</em> your project better?</li>
</ul>

<p>If you answered yes to any of these, you're not alone. And here's the thing—it's not the AI's fault. It's how we're feeding it information.</p>

<p>I spent months fighting with AI assistants, carefully crafting prompts, only to get generic code that didn't follow our architecture. Then I discovered context engineering, and everything changed.</p>

<hr>

<h2 id="thefundamentaldistinctionpromptengineeringvscontextengineering">🎯 The Fundamental Distinction: Prompt Engineering vs. Context Engineering</h2>

<p>Most developers focus on <strong>prompt engineering</strong>—crafting the perfect question to get the right answer. But there's a more powerful approach.</p>

<p><img src="https://blog.shanelee.name/content/images/2026/01/promptvscontext.png" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt1"></p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;"></th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Prompt Engineering</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Context Engineering</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">🔍 <strong>Focus</strong></td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Individual queries</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Information environment</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">📋 <strong>Scope</strong></td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">One-off instructions</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Persistent knowledge base</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">⚡ <strong>Effort</strong></td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Manual and repetitive</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Automated and consistent</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">📖 <strong>Analogy</strong></td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Telling a story each time</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">Building a library</td>
    </tr>
    <tr>
      <td style="padding: 12px;">📈 <strong>Impact</strong></td>
      <td style="padding: 12px;">Short-term results</td>
      <td style="padding: 12px;">Long-term productivity</td>
    </tr>
  </tbody>
</table>

<p>Here's the key insight:</p>

<blockquote>
  <p>💡 <strong>Prompt engineering is fighting the AI each time. Context engineering is teaching the AI once.</strong></p>
</blockquote>

<p>Think about onboarding a new developer. You don't explain your coding standards every time they write a function. You point them to documentation, show them examples, and let them absorb the patterns. Context engineering does the same thing for AI.</p>

<hr>

<h2 id="understandingcontextwindowsthehybridmodel">🧠 Understanding Context Windows: The Hybrid Model</h2>

<p>Every AI has a <strong>context window</strong>—its working memory. Claude 3.5 Sonnet has 200,000 tokens (roughly 150,000 words). That sounds like a lot, but it's finite and precious, like RAM in a computer.</p>

<p>The challenge: you can't include everything. So you need to be strategic.</p>

<p>The solution is a <strong>hybrid approach</strong>:</p>

<pre><code>┌──────────────────────────────┐
│  ⬆️  UPFRONT CONTEXT         │
│  • Standards &amp; conventions   │
│  • Architecture patterns     │
│  • Team workflows            │
├──────────────────────────────┤
│  ⚡ JUST-IN-TIME             │
│  • File contents (tools)     │
│  • External docs (MCPs)      │
│  • Real-time data (APIs)     │
└──────────────────────────────┘
</code></pre>

<blockquote>
  <p>🎯 <strong>The principle is simple:</strong> Give agents the RIGHT information at the RIGHT time, not ALL information ALL the time.</p>
</blockquote>

<p>Your project structure itself becomes a signal. Folder hierarchy teaches purpose. Naming conventions show role. Even timestamps signal patterns. A well-organized codebase is already teaching the AI.</p>

<hr>

<h2 id="thepowerofcontextarealexample">✨ The Power of Context: A Real Example</h2>

<p>Let me show you what this looks like in practice.</p>

<h3 id="withoutcontextengineering">❌ Without Context Engineering</h3>

<p>Request: <em>"Create a new user management API endpoint"</em></p>

<p>Result:  </p>

<pre><code class="language-python">from flask import jsonify  
from models import User

@app.route('/api/users', methods=['GET'])
def get_users():  
    users = User.query.all()
    return jsonify([u.__dict__ for u in users])
</code></pre>

<p><strong>The problems?</strong> Direct database access in the route, no service layer, no validation, exposes ORM objects directly, no error handling, no tests. It's generic code that doesn't follow your architecture.</p>

<h3 id="withcontextengineering">✅ With Context Engineering</h3>

<p>Same request: <em>"Create a new user management API endpoint"</em></p>

<p>Result:  </p>

<pre><code class="language-python"># src/api/routes/users.py
from fastapi import APIRouter, Depends  
from src.application.users.queries import GetUsersQuery  
from src.application.users.schemas import UserListResponse

router = APIRouter(prefix="/api/users", tags=["users"])

@router.get("/", response_model=UserListResponse)
async def get_users(query: GetUsersQuery = Depends()):  
    return await query.execute()

# src/application/users/queries.py
class GetUsersQuery:  
    def __init__(self, user_service: UserService = Depends()):
        self.user_service = user_service

    async def execute(self) -&gt; UserListResponse:
        users = await self.user_service.get_all()
        return UserListResponse(users=[UserDTO.from_entity(u) for u in users])
</code></pre>

<blockquote>
  <p>⏱️ <strong>Time saved: 30-60 minutes per feature.</strong></p>
</blockquote>

<p>The AI didn't get smarter. It just had the right context.</p>

<hr>

<h2 id="thefourpillarsofcontextengineering">🏛️ The Four Pillars of Context Engineering</h2>

<p>I've distilled context engineering into four pillars. Each serves a distinct purpose, and together they create an environment where AI becomes a true collaborator.</p>

<p><img src="https://blog.shanelee.name/content/images/2026/01/four-pillars-diagram.png" alt="Context Engineering: Stop Fighting Your AI and Start Shipping Faster Pt1"></p>

<h3 id="pillar1instructions">📜 Pillar 1: Instructions</h3>

<p><strong>Purpose:</strong> Project-wide persistent rules that the AI should always know.</p>

<pre><code>.github/
  copilot-instructions.md
  instructions/
    typescript.instructions.md
    react.instructions.md
    testing.instructions.md
</code></pre>

<p>This is where you document:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">✅ Coding standards and conventions</li>
  <li style="padding: 8px 0;">✅ Architecture patterns your team follows</li>
  <li style="padding: 8px 0;">✅ Testing requirements</li>
  <li style="padding: 8px 0;">✅ Common pitfalls to avoid</li>
</ul>

<blockquote>
  <p>📚 Think of instructions as the "employee handbook" for your AI assistant.</p>
</blockquote>

<hr>

<h3 id="pillar2chatmodespersonas">🎭 Pillar 2: Chatmodes (Personas)</h3>

<p><strong>Purpose:</strong> Role-based AI behavior for different tasks.</p>

<pre><code>.github/chatmodes/
  principal-engineer.chatmode.md
  code-reviewer.chatmode.md
  test-engineer.chatmode.md
</code></pre>

<p>Example chatmode:  </p>

<pre><code class="language-markdown">---
description: 'Thorough code reviewer'  
tools: ['editFiles', 'search']  
---
# Code Reviewer Mode

## Review Criteria
- Code quality and readability
- Performance implications
- Security vulnerabilities
- Test coverage
</code></pre>

<p>Different tasks need different mindsets. A code reviewer thinks differently than someone implementing a feature. Chatmodes let you switch the AI's perspective.</p>

<hr>

<h3 id="pillar3prompts">📝 Pillar 3: Prompts</h3>

<p><strong>Purpose:</strong> Standardize common tasks into reusable templates.</p>

<pre><code>.github/prompts/
  create-api-endpoint.prompt.md
  write-unit-tests.prompt.md
  refactor-component.prompt.md
</code></pre>

<p>Example prompt:  </p>

<pre><code class="language-markdown">---
mode: 'agent'  
description: 'Create REST API endpoint'  
---
# Create API Endpoint
1. Route handler in src/routes/  
2. Service layer in src/services/  
3. Add types in src/types/  
4. Unit + integration tests  
5. Update API docs  
</code></pre>

<p>Prompts ensure consistency. Every API endpoint follows the same structure. Every test file uses the same patterns. No more variation based on how you phrase the request.</p>

<hr>

<h3 id="pillar4mcpsmodelcontextprotocol">🔌 Pillar 4: MCPs (Model Context Protocol)</h3>

<p><strong>Purpose:</strong> Connect AI to external tools and authoritative sources.</p>

<p>Popular MCPs include:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🐙 <strong>GitHub</strong> → Repository data, PRs, issues</li>
  <li style="padding: 8px 0;">☁️ <strong>AWS</strong> → Well-Architected Framework, best practices</li>
  <li style="padding: 8px 0;">📋 <strong>Atlassian</strong> → Jira tickets, Confluence documentation</li>
  <li style="padding: 8px 0;">🐘 <strong>PostgreSQL</strong> → Direct database queries</li>
  <li style="padding: 8px 0;">📚 <strong>Context7</strong> → Up-to-date library documentation</li>
</ul>

<blockquote>
  <p>🔑 <strong>The key advantage:</strong> Instead of copying documentation into your prompt (which gets stale), MCPs give AI direct access to authoritative, always-current sources.</p>
</blockquote>

<hr>

<h2 id="arealworldstorythedockerchallenge">📖 A Real-World Story: The Docker Challenge</h2>

<p>Let me share how these four pillars came together for a real task.</p>

<h3 id="thechallenge">🎯 The Challenge</h3>

<p>I needed to document a business case for Docker hardened images across our infrastructure. The requirements:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🔧 Technical depth for engineers</li>
  <li style="padding: 8px 0;">💼 Business justification for leadership</li>
  <li style="padding: 8px 0;">☁️ Alignment with AWS Well-Architected Framework</li>
  <li style="padding: 8px 0;">📄 Delivered in Confluence</li>
  <li style="padding: 8px 0;">⏰ Timeline: 2-3 days</li>
</ul>

<h3 id="thetraditionalapproach">😓 The Traditional Approach</h3>

<pre><code>Day 1: Research (6-8 hours)  
Day 2: Draft document (6-8 hours)  
Day 3: Format and publish (4-6 hours)  
────────────────────────────────
Total: 16-22 hours 😱  
</code></pre>

<p>Plus 15-20 context switches, manual copy-paste between tools, and the inevitable documentation drift.</p>

<h3 id="thecontextengineeringapproach">🚀 The Context Engineering Approach</h3>

<p><strong>My setup:</strong></p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🎭 <strong>Chatmode:</strong> Principal Engineer (strategic thinking, architectural focus)</li>
  <li style="padding: 8px 0;">📜 <strong>Instructions:</strong> Business case template with our standard structure</li>
  <li style="padding: 8px 0;">🔌 <strong>MCPs:</strong> AWS Knowledge Base, Atlassian integration</li>
</ul>

<p><strong>My single request:</strong></p>

<blockquote>
  <p>"Create business case for Docker hardened images using AWS Well-Architected Framework. Publish to Confluence."</p>
</blockquote>

<p><strong>What happened:</strong></p>

<ol style="padding-left: 1.5em; margin: 1em 0;">  
  <li style="padding: 8px 0;">🔍 Agent queried AWS MCP for framework requirements</li>
  <li style="padding: 8px 0;">📋 Structured content using my business case template</li>
  <li style="padding: 8px 0;">✍️ Generated comprehensive documentation</li>
  <li style="padding: 8px 0;">📤 Published directly to Confluence via Atlassian MCP</li>
</ol>

<blockquote>
  <p>✨ <strong>Result: 2 days of work compressed into 4 hours of focused execution.</strong></p>
</blockquote>

<p>No context switching. No manual copy-paste. No gaps in framework coverage. Flow state maintained throughout.</p>

<hr>

<h2 id="keyprinciplesforeffectivecontextengineering">🎓 Key Principles for Effective Context Engineering</h2>

<p>Through trial and error, I've identified six principles that make context engineering work:</p>

<h3 id="1designfordiscoverability">1️⃣ Design for Discoverability</h3>

<p>Structure your project so both humans AND agents can navigate intuitively.</p>

<pre><code>src/features/auth/  
  components/  ← Purpose is clear
  services/    ← Logic lives here
  types/       ← Definitions grouped
</code></pre>

<blockquote>
  <p>💡 Good structure is good context.</p>
</blockquote>

<h3 id="2separatepersistentfromdynamic">2️⃣ Separate Persistent from Dynamic</h3>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;"><strong>Persistent (upfront):</strong> Standards, patterns, workflows—things that rarely change</li>
  <li style="padding: 8px 0;"><strong>Dynamic (just-in-time):</strong> File contents, external docs, real-time data—things that change frequently</li>
</ul>

<p>Don't bloat your context window with information that should be fetched on demand.</p>

<h3 id="3leveragespecializedtools">3️⃣ Leverage Specialized Tools</h3>

<p>Don't make AI memorize documentation. Connect it to the source.</p>

<table style="width:100%; border-collapse: collapse; margin: 1.5em 0;">  
  <thead>
    <tr style="background-color: #f4f4f4;">
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">Before</th>
      <th style="padding: 12px; text-align: left; border-bottom: 2px solid #ddd;">With MCP</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">📋 Paste 10,000 words of AWS docs</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">🔌 "Use AWS Container Lens best practices"</td>
    </tr>
    <tr>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">❌ Gets stale</td>
      <td style="padding: 12px; border-bottom: 1px solid #eee;">✅ Always current</td>
    </tr>
    <tr>
      <td style="padding: 12px;">❌ Eats context window</td>
      <td style="padding: 12px;">✅ Fetched on demand</td>
    </tr>
  </tbody>
</table>

<h3 id="4iterateandmeasure">4️⃣ Iterate and Measure</h3>

<p>Track what matters:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">⏱️ Time saved per task</li>
  <li style="padding: 8px 0;">⭐ Quality improvements</li>
  <li style="padding: 8px 0;">🔄 Number of iterations needed</li>
  <li style="padding: 8px 0;">😊 Developer satisfaction</li>
</ul>

<blockquote>
  <p>📊 What gets measured gets improved.</p>
</blockquote>

<h3 id="5showdontjusttell">5️⃣ Show, Don't Just Tell</h3>

<p>Few-shot prompting works. Instead of writing a laundry list of rules, provide 3-5 high-quality examples showing expected behavior.</p>

<blockquote>
  <p>🖼️ For AI, examples are worth a thousand words.</p>
</blockquote>

<h3 id="6contextcompounds">6️⃣ Context Compounds</h3>

<p>Better context leads to better results. Better results help you refine context. The cycle accelerates.</p>

<pre><code>     ┌─────────────┐
     │   Better    │
     │   Context   │
     └──────┬──────┘
            │
            ▼
     ┌─────────────┐
     │   Better    │
     │   Results   │
     └──────┬──────┘
            │
            ▼
     ┌─────────────┐
     │  Refined    │
     │   Context   │◄──── 🔄 The cycle continues!
     └─────────────┘
</code></pre>

<p>This is why the initial investment pays dividends over time.</p>

<hr>

<h2 id="theroiwhythismatters">💰 The ROI: Why This Matters</h2>

<p>Let's talk numbers:</p>

<pre><code>⏱️ Time Investment:
├─ Initial setup: 2-3 hours
├─ Weekly maintenance: 30 minutes
└─ Quarterly review: 1 hour

💵 Weekly Savings: 8-17 hours recovered

🎉 Annual ROI: 400-800 hours saved
</code></pre>

<blockquote>
  <p>🚀 That's <strong>10-20 weeks of productive time</strong> returned to you every year!</p>
</blockquote>

<p>But it's not just about time. It's about:</p>

<ul style="list-style: none; padding-left: 0; margin: 1em 0;">  
  <li style="padding: 8px 0;">🎯 <strong>Consistency:</strong> Every feature follows your patterns</li>
  <li style="padding: 8px 0;">⭐ <strong>Quality:</strong> Production-ready code from the start</li>
  <li style="padding: 8px 0;">🧘 <strong>Flow:</strong> No more context switching between docs and tools</li>
  <li style="padding: 8px 0;">👥 <strong>Onboarding:</strong> New team members (human and AI) ramp up faster</li>
</ul>

<hr>

<h2 id="theparadigmshift">🌟 The Paradigm Shift</h2>

<p>Here's what I've come to believe:</p>

<blockquote>
  <p><em>"Context engineering represents a fundamental shift in how we build with LLMs. As models become more capable, the challenge isn't just crafting the perfect prompt—it's thoughtfully curating what information enters the model's limited attention budget at each step."</em></p>
</blockquote>

<p>This isn't about more prompting. It's about <strong>better context</strong>.</p>

<p>This isn't about AI as a tool. It's about <strong>AI as an informed collaborator</strong>.</p>

<p>The developers who master context engineering won't just be more productive—they'll be working in a fundamentally different way. One where AI doesn't fight them, but works alongside them with full understanding of the codebase, the patterns, and the goals.</p>

<hr>

<h2 id="yournextstep">🎬 Your Next Step</h2>

<p>Start with one instruction file. Document your coding standards. Watch how the AI's output improves.</p>

<p>Then iterate.</p>

<blockquote>
  <p>✨ <strong>The best context is the one that evolves with your team.</strong></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[Dependency management with dependabot]]></title><description><![CDATA[How to shift security left and manage dependencies via dependabot and reduce the no. of security vulnerabilities.]]></description><link>https://blog.shanelee.name/2023/02/28/dependency-management-with-dependabot/</link><guid isPermaLink="false">d169802e-3e11-47a5-81ad-73cfcd7c3ff0</guid><category><![CDATA[github]]></category><category><![CDATA[devsecops]]></category><category><![CDATA[dependabot]]></category><category><![CDATA[owasp]]></category><category><![CDATA[security]]></category><category><![CDATA[culture]]></category><category><![CDATA[leadership]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Tue, 28 Feb 2023 03:30:31 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2023/02/guerrillabuzz-blockchain-pr-agency-SYofhg_IX3A-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2023/02/guerrillabuzz-blockchain-pr-agency-SYofhg_IX3A-unsplash.jpg" alt="Dependency management with dependabot"><p>We all know how vital shifting security left is and how engineering needs to work more closely with security. According to the Cloud Security Alliance (CSA), 70% of <a href="https://cloudsecurityalliance.org/artifacts/secure-devops-and-misconfigurations-survey-report/">security professionals and engineering teams</a> struggle to <strong>“shift left,”</strong> with many unable to recognize the formation of anti-patterns and understand/appreciate the wider impact to development, cost, governance, culture, etc.</p>

<h2 id="recognizingtheparadigmshift">Recognizing the Paradigm Shift</h2>

<p>Engineering teams that implement DevSecOps practices and automate security tooling will <mark>discover security risks earlier, saving developers time, accelerating release cycles, and shipping more secure and compliant code</mark>.</p>

<p>As I have previously <a href="https://blog.shanelee.name/2022/11/04/shifting-security-left/">discussed</a>, dependency management is an important part of securing your software supply chain. This post covers how to roll out Dependabot internally within your company to keep your dependencies up to date.</p>

<p><mark>NB: There are other tools such as Renovate and Snyk, but as Dependabot is native to the GitHub platform I am focusing the talk on this tool.</mark></p>

<p>Keeping your dependencies up to date is one of the easiest ways to keep your systems secure. The issue of supply chain security has become increasingly obvious in the past number of years, from the malicious <code>flatmap-stream</code> package to the most recent <code>log4shell</code> vulnerabilities. <a href="https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts">Dependabot</a> will alert developers when a repository is using a software dependency with a known vulnerability. By rolling out Dependabot internally to all of your repositories, you can measure, and significantly reduce, your usage of software dependencies with known vulnerabilities.</p>

<p>Everyone will remember the security breach at <a href="https://www.infoq.com/news/2017/09/struts/">Experian</a> in 2017. Hackers stole the personal details of <strong>143 million Americans</strong> from the Equifax credit report company after exploiting a security flaw in the Apache Struts framework. That is why it is so important to keep track of your dependencies and any known vulnerabilities.</p>

<p>OWASP published last year their top ten, and vulnerable and outdated components moved up to <a href="https://owasp.org/Top10/A06_2021-Vulnerable_and_Outdated_Components/">sixth position</a>. Every organization must ensure an ongoing plan for monitoring, triaging, and applying updates or configuration changes for the lifetime of the application or portfolio. There are over 20,000 common vulnerabilities and exposures (CVEs) discovered per year in open-source and third-party code.</p>

<p><img src="https://blog.shanelee.name/content/images/2023/02/owasp.png" alt="Dependency management with dependabot"></p>

<h2 id="empoweringdeveloperstobesecurityminded">Empowering developers to be security minded</h2>

<p>According to the SANS 2022 DevSecOps Survey: <a href="https://www.deepfactor.io/sans-2022-devsecops-survey/">Creating a Culture to Significantly Improve Your Organization’s Security Posture</a>, “management buy-in” was the number one factor contributing to DevSecOps security programs’ success.</p>

<p><img src="https://blog.shanelee.name/content/images/2023/02/image2.png" alt="Dependency management with dependabot"></p>

<h2 id="whatisdependabot">What is Dependabot?</h2>

<p><img src="https://blog.shanelee.name/content/images/2023/02/depend.png" alt="Dependency management with dependabot"></p>

<p>There are a number of guiding principles when evaluating tools and designing a rollout plan. For example, Does the security benefit of this new process outweigh the impact on engineering teams? How do we roll this out incrementally and gather feedback? What are our expectations for engineers, and how do we clearly communicate these expectations?</p>

<p>For Dependabot in particular, some of these questions are easy to answer. Dependabot is a native feature of GitHub, meaning, that it integrates with your engineers’ current workflows on GitHub.com. By better tracking the security of your software supply chain, you will keep your software secure, which outweighs any potential impact on engineering teams.</p>

<p>When Dependabot raises pull requests, these pull requests could be for <code>security</code> or <code>version</code> updates:</p>

<ul>
<li><strong>Dependabot security updates</strong> are automated pull requests that help you update dependencies with known vulnerabilities. </li>
<li><strong>Dependabot version updates</strong> are automated pull requests that keep your dependencies updated, even when they don’t have any vulnerabilities. To check the status of version updates, navigate to the Insights tab of your repository, then Dependency Graph, and Dependabot.</li>
<li>Dependabot security updates may include <strong>compatibility scores</strong> to let you know whether updating a dependency could cause breaking changes to your project.</li>
</ul>

<h3 id="caveats">Caveats</h3>

<ul>
<li><strong>Dependabot alerts</strong> - Owners of private repositories, or people with admin access, can enable Dependabot alerts by enabling the dependency graph and Dependabot alerts for your repositories. </li>
<li><strong>Secrets</strong> - When a Dependabot event triggers a workflow, the only secrets available to the workflow are Dependabot secrets. The simplest solution is to store the token with the permissions required in an action and in a Dependabot secret with <em>identical names</em>.</li>
<li><strong><a href="https://semver.org/">Semantic versioning</a></strong> – don’t trust that every third party dependency aligns with this!</li>
<li><strong>Vulnerability database</strong> -  GitHub's security features do not claim to catch all vulnerabilities and malware. They actively maintain <em>GitHub Advisory Database</em> and generate alerts with the most up-to-date information. However, they <em>cannot catch everything</em> or tell you about known vulnerabilities within a guaranteed time frame. These features are not substitutes for a human review of each dependency for potential vulnerabilities or any other issues, and they recommend consulting with a security service or conducting a thorough dependency review when necessary.</li>
</ul>

<h3 id="bewareofdependencyfatigue">Beware of dependency fatigue</h3>

<p>You may have heard of <a href="https://www.pagerduty.com/blog/cutting-alert-fatigue-modern-ops/">alert fatigue</a> before when you are on a support roster. Well, the same can be said for dependency management: I call it <mark>dependency fatigue</mark>! The last thing you want is for teams to not configure dependabot properly and start to ignore the PRs and alerts created or turn them off completely. So what can we do to help alleviate this? Below I provide some tips and tricks I have used in the past and some sample configs.</p>

<h3 id="tips">Tips</h3>

<ul>
<li>Design config with your team - Get the team to review the configuration and agree on the setup.</li>
<li>Understand what <code>package-ecosystem(s)</code> to enable (docker, maven, actions, terraform etc)</li>
<li>Add <code>labels</code> to your PRs to allow easier filtering</li>
<li>Prioritise the order and schedule</li>
<li>Assign ownership to who is on call</li>
<li>If your dependencies are very outdated, you might want to start with a daily schedule until the dependencies are up-to-date, and then drop back to a weekly schedule.</li>
<li>Review the changelog (look for any breaking changes announced) and make sure checks pass on your branch</li>
<li>It's good practice to have automated tests and acceptance processes in place so that checks are carried out before the pull request is merged.</li>
<li>Test and learn! 😅</li>
</ul>

<h3 id="sampleconfig">Sample config</h3>

<p>You can place this file <code>dependabot.yml</code> under your <code>.github</code> directory in your repo.</p>

<p>This example below showcases a repo that is interested in two eco-systems: <code>docker</code> and <code>actions</code>. I have added in a schedule for docker updates in my local timezone of Melbourne. And I have added some labels. This helps to filter your PRs if you want to focus on specific updates. The <code>open-pull-requests-limit</code> default is 5, so in this instance, I have reduced it to 2. </p>

<p><img src="https://blog.shanelee.name/content/images/2023/02/code.png" alt="Dependency management with dependabot"></p>

<p>The next example showcases a sample NodeJS project. Again you are interested in updates from two ecosystems: <code>npm</code> and <code>actions</code>. What this config is also showcasing is how you can ignore certain dependencies. You can see that it ignores packages that start with <code>aws</code>, ignores updates to <code>express</code> and patch updates for <code>all</code> dependencies. </p>

<p><img src="https://blog.shanelee.name/content/images/2023/02/code1.png" alt="Dependency management with dependabot"></p>

<p>Lastly, is a sample config for a JVM-based project. Here we are covering three package ecosystems as it's a cloud-native microservice. It covers updates to <code>maven</code>, <code>docker</code> and <code>actions</code>. </p>

<p><img src="https://blog.shanelee.name/content/images/2023/02/code3.png" alt="Dependency management with dependabot"></p>

<p>Below is a sample PR that was created by dependabot based on the config above. You can see it has added the labels of <code>dependencies</code> and <code>maven</code>. It provides the release notes and changelog for you to review. </p>

<p><img src="https://blog.shanelee.name/content/images/2023/03/Screen-Shot-2023-03-02-at-10-06-07-am.png" alt="Dependency management with dependabot"></p>

<p>If you want to know more about the configuration options for dependabot, check out the doc <a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file">here</a></p>

<h2 id="rollout">Rollout</h2>

<p>Dependabot, like other GitHub Advanced Security features, can be enabled for all repositories within an organization from the organization’s administration page. Depending on how you create new GitHub repositories (infra as code anyone??), you can also enable upon creation by <a href="https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-security-and-analysis-settings-for-your-organization#enabling-or-disabling-a-feature-automatically-when-new-repositories-are-added">default</a>. That way there is no major impact on teams and a staged rollout is not needed. Frequent company-wide comms are recommended.</p>

<p>The aim is to answer, clearly and succinctly, the most important questions in your communications: <code>What are we doing? Why are we doing this? When are we doing this? Lastly, what do I need to do?</code> The last question was key. Make it clear that you are rolling out Dependabot organization-wide to understand your current risk and that, while we encourage service owners to upgrade dependencies, you are not expecting every Dependabot alert to be fixed right away.</p>

<p>Running an engineering workshop on dependency management and dependabot can help educate teams on the importance of this initiative and shifting security left. Showcasing how to manage security and version updates for your services via dependabot will set the foundations and kick-start the conversation. Like any change management framework, it is important to <code>communicate/educate</code>, <code>build the capability</code> and <code>change the mindsets</code>.</p>

<h2 id="remediation">Remediation</h2>

<p>Once you had Dependabot enabled for all your GitHub repositories, you could measure the general trend of Dependabot alerts across the company. At an org level under the <code>insights</code> tab, you have a bird's eye view of dependencies and open security advisories. You can filter by most critical and then switch your focus from measuring the current state to working with repository owners to upgrade their dependencies.  By leveraging insights, you can ensure that you focus your remediation efforts on repositories that are running in production, where a vulnerable dependency could present a risk to your company and your users. As I mentioned earlier, <strong>"management buy-in"</strong> is extremely important in incorporating dependency management into your security program. Otherwise, you will end up competing with feature delivery and it will fall further down the pecking order. Security trumps customer centricity.</p>

<p><img src="https://blog.shanelee.name/content/images/2023/03/Screenshot-2023-02-28-at-3-33-51-pm.png" alt="Dependency management with dependabot"></p>

<p>If you have a platform engineering team you could go even further and build or use a nice internal service catalog tool like <a href="https://backstage.spotify.com/">backstage</a> and hook into GitHub graphQL API to track this. As part of measuring system health and running an internal security program, you can work with your stream-aligned teams to define <em>SLOs</em> and <em>SLIs</em> regarding timeframes to resolve. An example could be: <br>
<strong>"Critical and high severity vulnerable dependencies are fixed within 30 days"</strong>.</p>

<p><img src="https://blog.shanelee.name/content/images/2023/02/Screenshot-2023-02-28-at-11-42-52-am.png" alt="Dependency management with dependabot"></p>

<p>It is important to acknowledge that <code>not all Dependabot alerts</code> can be actioned immediately. Instead, assign a realistic grace period for service owners to remediate Dependabot alerts before marking a metric as failing.</p>

<h2 id="outcomes">Outcomes</h2>

<p>The goal here is to reduce the no. of Dependabot alerts over time for your services. But do not regard this as a <strong>once-off task</strong>. Rather, you need to follow the dependabot alert metrics continuously over time and intervene if you see them trending in the wrong direction. This will help you have a better picture of your system health across the company and prioritise work.</p>

<p>Happy updating! 😀</p>]]></content:encoded></item><item><title><![CDATA[Shifting security left]]></title><description><![CDATA[<p>As part of adopting CD (Continuous Delivery) and embracing a DevOps culture, shifting security left is one of the capabilities needed to drive high software delivery and organisational performance.</p>

<p>Tech companies have identified that security is EVERYONE's responsibility. ie. Security is a distributed ownership model. What <strong>shifting left</strong> means is</p>]]></description><link>https://blog.shanelee.name/2022/11/04/shifting-security-left/</link><guid isPermaLink="false">fedd7be5-31ee-4340-9199-bc0eb054c558</guid><category><![CDATA[github]]></category><category><![CDATA[culture]]></category><category><![CDATA[aws]]></category><category><![CDATA[devops]]></category><category><![CDATA[devsecops]]></category><category><![CDATA[security]]></category><category><![CDATA[owasp]]></category><category><![CDATA[dependabot]]></category><category><![CDATA[renovate]]></category><category><![CDATA[team-topologies]]></category><category><![CDATA[checkov]]></category><category><![CDATA[actions]]></category><category><![CDATA[tflint]]></category><category><![CDATA[terraform]]></category><category><![CDATA[tfsec]]></category><category><![CDATA[devX]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Fri, 04 Nov 2022 08:43:55 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2022/11/matthew-henry-fPxOowbR6ls-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2022/11/matthew-henry-fPxOowbR6ls-unsplash.jpg" alt="Shifting security left"><p>As part of adopting CD (Continuous Delivery) and embracing a DevOps culture, shifting security left is one of the capabilities needed to drive high software delivery and organisational performance.</p>

<p>Tech companies have identified that security is EVERYONE's responsibility. ie. Security is a distributed ownership model. What <strong>shifting left</strong> means is including concerns such as security earlier in the development lifecycle.</p>

<blockquote>
  <p>The principles of shifting left also apply to security, not only to operations. It’s critical to prevent breaches before they can affect users, and to move quickly to address newly discovered security vulnerabilities and fix them.</p>
</blockquote>

<p>In software development, there are at least these four activities: design, develop, test, and release. In a traditional software development cycle, testing (including security testing), happens after development is complete. This typically means that a team discovers significant problems, including architectural flaws, that are expensive to fix.</p>

<p>After defects are discovered, developers must then find the contributing factors and how to fix them. In complex production systems, it's not usually a single cause; instead, it's often a series of factors that interact to cause a defect. Defects involving security, performance, and availability are expensive and <a href="https://linearb.io/blog/engineering-metrics-benchmarks-what-makes-elite-teams/#:~:text=PR%20review%20process.-,Rework%20Rate,-%3A">time-consuming</a> to remedy; they often require architectural changes. The time required to find the defect, develop a solution, and fully test the fix are unpredictable. This can further push out delivery dates.</p>

<p>Research from <a href="https://www.infoq.com/news/2022/10/google-devops-2022/">Accelerate State of DevOps Research and Assessment 2022</a> (DORA) shows that teams can achieve better outcomes by making security a part of everyone's daily work, instead of testing for security concerns at the end of the process. <mark>One key finding is that the largest predictor of an organization's software security practices was not technical but instead <code>cultural</code>.</mark> Leveraging Westrum's organizational topology, high-trust, low-blame cultures focused on performance were significantly more likely to adopt emerging security practices than low-trust, high-blame cultures that focused on power or rules. For more on organisational culture and my assessment of the book Accelerate check out my previous <a href="https://blog.shanelee.name/2022/05/15/book-review-accelerate-and-my-experience-in-high-performing-organisations/">blog post</a></p>

<h2 id="howtoimplementimprovedsecurityquality">How to implement improved security quality</h2>

<h3 id="getinfosecinvolvedinsoftwaredesign">Get Infosec involved in software design</h3>

<p>The Infosec team should get involved in the design phase for all projects. As you can imagine, the ratio of engineers/builders to security within a company rates pretty high. So companies these days need to rethink how to approach shifting security left. I found this great article on how a company adopted the team's topology approach and looked to re-frame security as an <a href="https://www.securitydifferently.com/minimum-viable-security-knowledge-and-team-topologies-for-security/">enablement team</a>.</p>

<blockquote>
  <p>This mode helps reduce gaps in capabilities and is suited to situations where one or more teams would benefit from the active help of another team facilitating or coaching an aspect of their work. This is the primary operating mode of an enabling team and provides support to many other teams. It can also help discover gaps or inconsistencies in existing components and services used.</p>
</blockquote>

<p>Educating teams on <a href="https://thenewstack.io/the-latest-owasp-top-10-looks-a-lot-like-the-old-owasp/">OWASP 10</a> and <a href="https://apisecurity.io/encyclopedia/content/owasp/owasp-api-security-top-10.htm">API security</a> is extremely important. Understanding AuthZ, excessive data exposure and insufficient logging and monitoring remain high on the list. </p>

<h3 id="educationprograms">Education Programs</h3>

<p>Companies like AWS have created a guardian program whereby every stream-aligned team needs a security guardian (or what I like to call a security “champion”). So you cannot deliver value to your customers (release to prod) until these criteria are met. No guardian, no shipping software! 😛</p>

<p>The program consists of workshops and training from the security team (OWASP, threat modelling, AWS Well-Architected framework (security pillar) etc). Due to the ratio of engineers to security, they regard the security team as an <code>enablement</code> function. This ties back to team topologies again and its four-team types (enablement/stream aligned/platform/complicated sub-system).</p>

<blockquote>
  <p>Instead of “us versus them,” make security part of development from the start, and encourage day-to-day collaboration between both teams.</p>
</blockquote>

<h2 id="securityandcompliance">Security and compliance</h2>

<h3 id="threatmodelling">Threat modelling</h3>

<p><img src="https://blog.shanelee.name/content/images/2022/11/cupcake-256x256.png" alt="Shifting security left"></p>

<p>I have observed companies adopting a threat modelling framework as part of the assessment. <a href="https://www.appsecengineer.com/blog/what-is-threat-modeling-how-do-you-learn-it">Frameworks</a> such as <code>STRIDE</code> and <code>PASTA</code> are quite popular. OWASP for example has a modelling tool called <a href="https://owasp.org/www-project-threat-dragon/">Threat Dragon</a> that supports frameworks like <code>STRIDE</code>.</p>

<p>Threat modelling is a set of techniques to help you identify and classify potential threats during the development process — but I want to emphasize that this is not a <strong>one-off activity</strong> only done at the start of projects.</p>

<p>This is because throughout the lifetime of any software, new threats will emerge and existing ones will continue to evolve thanks to external events and ongoing changes to requirements and architecture. This means that threat modelling needs to be <strong>repeated periodically</strong> — the frequency of repetition will depend on the circumstances and will need to consider factors such as the cost of running the exercise and the potential risk to the business. When used in conjunction with other techniques, such as establishing cross-functional security requirements to address common risks in the project's technologies and using automated security scanners, threat modelling can be a powerful asset.</p>

<p>AWS provides <a href="https://catalog.workshops.aws/threatmodel/en-US">workshops</a> on threat modelling and educating teams on the approach. GitHub talks about its threat modelling <a href="https://github.blog/2020-09-02-how-we-threat-model/">process</a> and how it brings more improved communication between security and the engineering teams and how they are able to "shift left".</p>

<h3 id="bugbountyprograms">Bug bounty programs</h3>

<p><img src="https://blog.shanelee.name/content/images/2022/11/download.jpeg" alt="Shifting security left"></p>

<p>Many companies are now adopting and investing in bug bounty programs. Bug bounty programs give you continuous, real-time vulnerability insights across your expanding digital attack surface so you can eliminate critical threat “blind spots” and strengthen your security posture.</p>

<p>Check out this interview with one of the security researchers in the <a href="https://github.blog/2022-10-28-cybersecurity-spotlight-on-bug-bounty-researcher-ahacker1/">Github Security Bug Bounty program</a> to understand more about their background and how they keep up with the latest vulnerability trends. One program I have used in the past is <a href="https://www.bugcrowd.com/">BugCrowd.</a></p>

<p><a href="https://en.wikipedia.org/wiki/Capture_the_flag_(cybersecurity)">CTF</a> (<strong>Capture The Flag</strong>) competitions are also a great way to keep engineers engaged (security mindset).</p>

<p>Adopting an <em>NFR (Non-Functional Requirements) checklist</em> in the design phase helps to deliver quality at speed; incorporating performance, security and reliability. One example of compliance is the <strong>4-eyes principle</strong> where at least <em>two</em> approvers are needed before merging the PR. <a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners">CODEOWNERS</a> helps automatically assign individuals (or teams) to review PRs. Below I will elaborate further on the tooling that can enable and automate compliance as part of the development lifecycle early.</p>

<h2 id="securityapprovedtools">Security-approved tools</h2>

<p>Security-approved tools help to integrate and automate many tooling into your day-to-day life as an engineer. I have noticed over time the rapid rise of Devsecops and tooling that can help achieve this. The areas that I used and have observed include (<em>non-exhaustive list</em>):</p>

<h3 id="infraascodescanning">Infra as code scanning</h3>

<ul>
<li><a href="https://github.com/aquasecurity/tfsec">Tfsec</a> - Uses static analysis of your terraform code to spot potential misconfiguration. Can be easily incorporated into your CI pipeline.</li>
</ul>

<p><img src="https://blog.shanelee.name/content/images/2022/11/example-github-pr-check.png" alt="Shifting security left"></p>

<ul>
<li><a href="https://www.checkov.io/">Checkov by Bridgecrew</a> - Can scan results across platforms such as Terraform, CloudFormation, Kubernetes, Helm, ARM Templates and Serverless framework.</li>
<li><a href="https://snyk.io/product/infrastructure-as-code-security/">Snyk</a> supports many formats and can also detect drifts post-deployment.</li>
<li>Policy enforcement for terraform using <a href="https://www.conftest.dev/">conftest</a> - Doordash have a great <a href="https://doordash.engineering/2022/09/20/how-doordash-ensures-velocity-and-reliability-through-policy-automation/">article</a> on how they ensure reliability and velocity through conftest and Atlantis. </li>
</ul>

<h3 id="bots">Bots</h3>

<p><img src="https://blog.shanelee.name/content/images/2022/11/1_MARssNdoQ3kG1mdZEDV3iA.png" alt="Shifting security left"></p>

<p>I am a big fan of chatbots! I watched a presentation from a company recently that built multiple chatbots on shifting security left and helping with remediation. </p>

<p>They had an intern with an interest in python and he created some very nifty chatbots. <br>
Behind the scenes, they use Cloud conformity from Trend Micro. Now when an engineer is naughty and maybe clickops and opens a security group to the world or an s3 bucket, the bot will alert the channel and notify of the issue in real-time (they only broadcast on high and mediums). You can then ask the bot also who made the change. It will then scan Cloudtrail, find the IAM user in question and link it back to the slack handle of the user and post back to the channel who's to “blame” (more in the sense of git blame than to literally blame!). The bot could also perform auto-remediation. This is just an example of the opportunities that exist with chatbots.</p>

<h3 id="lintingcompliance">Linting (Compliance)</h3>

<p><img src="https://blog.shanelee.name/content/images/2022/11/icon-512.png" alt="Shifting security left"></p>

<p>Linters are tools used to flag programming errors, bugs, stylistic errors and suspicious constructs. Examples include:</p>

<ul>
<li><a href="https://eslint.org/">Eslint</a> - is an open-source project that helps you find and fix problems with your JavaScript code.</li>
<li><a href="https://github.com/terraform-linters/tflint">TFlint</a> is a pluggable terraform linter that finds possible errors (like invalid instance types) for Major Cloud providers (AWS/Azure/GCP) and enforces best practices.</li>
<li><a href="https://github.com/rhysd/actionlint">Actionlint</a> is a static checker for GitHub workflow files. I use the VSCode extension extensively.</li>
<li><a href="https://github.com/adrienverge/yamllint">yamllint</a> is a linter for YAML files.</li>
<li><a href="https://stoplight.io/open-source/spectral">oaslint</a> - Spectral is an OAS linter to enforce best practices for API design of Open API Specs. I have introduced this linter before at organisations to align with companies' API guidelines and catch any inconsistencies early in the development lifecycle.</li>
</ul>

<p>If you are using GitHub actions check out this awesome list of <a href="https://github.com/sdras/awesome-actions#linting">linters</a> that the community recommends.</p>

<h3 id="securityversionupdates">Security &amp; Version updates</h3>

<p><img src="https://blog.shanelee.name/content/images/2022/11/dependabot.png" alt="Shifting security left"></p>

<p>OWASP published last year their top ten, and <strong>vulnerable and outdated components</strong> moved up to <a href="https://owasp.org/Top10/A06_2021-Vulnerable_and_Outdated_Components">sixth position</a></p>

<p><a href="https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts">Dependabot</a> and <a href="https://docs.renovatebot.com/#why-use-renovate">Renovate</a> are two tools I have used in the past for dependency updates and vulnerability management. Dependabot is part of the GitHub ecosystem now and is maturing every day. But I do miss the nice presets that Renovate has and how you can group certain dependencies too.</p>

<p>One lesson I have learnt from automating this process is to be aware of <em>dependency fatigue</em>. Work with the team on your schedule and what dependencies are the highest priority. Otherwise, you could end up becoming overwhelmed with PRs and start to ignore updates!</p>

<p><a href="https://github.blog/2022-05-25-how-we-use-dependabot-to-secure-github/">Github</a> talks about how they use dependabot internally to secure their own platform.</p>

<p>OWASP <a href="https://owasp.org/www-project-dependency-check/">dependency checker</a> is also a useful tool to run as part of your CI pipeline to identify security vulnerabilities.</p>

<h3 id="codescanningsast">Code scanning (SAST)</h3>

<p><img src="https://blog.shanelee.name/content/images/2022/11/dev-cycle.png" alt="Shifting security left"></p>

<p><a href="https://www.sonarqube.org/">SonarQube</a> is a self-managed, automatic code review tool that systematically helps you deliver Clean Code. As a core element of their solution, SonarQube integrates into your existing workflow and detects issues in your code to help you perform continuous code inspections of your projects. The tool analyses <em>30+ different programming languages</em> and integrates into your CI pipeline and DevOps platform to ensure that your code meets high-quality standards.</p>

<h3 id="containerscanning">Container scanning</h3>

<ul>
<li><p><a href="https://docs.paloaltonetworks.com/prisma/prisma-cloud/prisma-cloud-admin-compute/tools/twistcli">Twistlock</a> is now part of Palo Alto’s Prisma Cloud offering and is one of the leading container security scanning solutions.</p></li>
<li><p><a href="https://docs.snyk.io/products/snyk-container/how-snyk-container-works">Snyk</a> also provides a similar product offering to scan images.</p></li>
</ul>

<p>I hope you have found this list of security tools useful. Head over to <a href="https://landscape.cncf.io/card-mode?category=security-compliance,security">CNCF</a> to find more.</p>

<p>Suffice it to say are we still shifting left? Is it realistic to expect developers to take on the burdens of security and infrastructure provisioning, as well as writing their applications? Is platform engineering the answer to saving the DevOps dream?? I will leave you with that thought! 😉</p>]]></content:encoded></item><item><title><![CDATA[Book review: Accelerate]]></title><description><![CDATA[Review of Accelerate, high performing organizations and the four key metrics of change lead time, deployment frequency, mean time to recover, and change failure rate.]]></description><link>https://blog.shanelee.name/2022/05/15/book-review-accelerate-and-my-experience-in-high-performing-organisations/</link><guid isPermaLink="false">9d648e15-7d5f-473f-bc43-bcb2d8e354d8</guid><category><![CDATA[accelerate]]></category><category><![CDATA[devops]]></category><category><![CDATA[culture]]></category><category><![CDATA[leadership]]></category><category><![CDATA[CD]]></category><category><![CDATA[continuous delivery]]></category><category><![CDATA[high-performing]]></category><category><![CDATA[transformation]]></category><category><![CDATA[tracing]]></category><category><![CDATA[docker]]></category><category><![CDATA[github]]></category><category><![CDATA[aws]]></category><category><![CDATA[cloud]]></category><category><![CDATA[google]]></category><category><![CDATA[CI]]></category><category><![CDATA[fourkeymetrics]]></category><category><![CDATA[lean]]></category><category><![CDATA[agile]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Sun, 15 May 2022 07:27:53 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2022/05/jacek-dylag-fZglO1JkwoM-unsplash--1-.jpg" medium="image"/><content:encoded><![CDATA[<h1 id="overview">Overview</h1>

<img src="https://blog.shanelee.name/content/images/2022/05/jacek-dylag-fZglO1JkwoM-unsplash--1-.jpg" alt="Book review: Accelerate"><p>In the book, <em>Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations</em>, Dr. Nicole Forsgren, Jez Humble, and Gene Kim studied what made separated strong performing technology organizations from their less effective counterparts.</p>

<p>The book summarises <strong>4 years</strong> of rigorous research from years of State of DevOps Reports, built upon <strong>23,000 datasets</strong> from over <strong>2,000 unique companies</strong> all around the world. The organizations studied included start-ups and enterprises, profit and not-for-profit organisations, and companies that were born digital alongside those that had to undergo digital transformation.</p>

<h2 id="fourkeymetrics">Four key metrics</h2>

<p>The research identified that just <strong>Four Key Metrics</strong> distinguish the performance of various organisations. These “North Star” metrics serve as indicators of overall software engineering health. <br>
These metrics aren’t “The Goal” of a business, but organisations that did well against these metrics had higher rates of <strong>profitability 💵, market share, and customer satisfaction</strong>. In other words; they allowed organisations to <strong>experiment faster 🚀, ship reliably, and prevent burnout</strong>.</p>

<p>The Four Key Metrics were as follows:</p>

<ul>
<li><mark>Cycle Time (Change Lead Time)</mark> - Time to implement, test, and deliver code for a feature (measured from first commit to deployment)</li>
<li><mark>Deployment Frequency</mark> - Number of deployments in a given duration of time</li>
<li><mark>Change Failure Rate (CFR)</mark> - Percentage of deployments which caused a failure in production</li>
<li><mark>Mean Time to Recovery (MTTR)</mark> - Mean time it takes to restore service after production failure</li>
</ul>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-15-at-6-10-07-pm.png" alt="Book review: Accelerate"></p>

<h2 id="whythesemetrics">Why these metrics</h2>

<p>Where is the evidence that these metrics help organisational performance though? How do you convince your boss that these things matter??</p>

<h3 id="martinfowleronacceleratemetrics">Martin Fowler on Accelerate metrics</h3>

<p>The picture they paint is compelling. They describe how effective IT delivery organizations take about an hour to get code from <strong>"committed to mainline"</strong> to <strong>"running in production"</strong>, a journey lesser organizations take months to do. They, thus, update their software many times a day instead of once every few months, increasing their ability to use software to explore the market, respond to events, and release features faster than their competition. This huge increase in responsiveness does not come at a cost in stability, since these organizations find their updates cause failures at a fraction of the rate of their less-performing peers, and these failures are usually fixed within the hour. Their evidence refutes the bimodal IT notion that you have to choose between <strong>speed and stability</strong> instead, speed <strong>depends</strong> on stability, so good IT practices give you both.</p>

<p>So, as you may expect, I'm delighted that they've put this book into production, and I will be recommending it willy-nilly over the next few years. (I've already been using many bits from its drafts in my talks.) However, I do want to put in a few notes of caution. They do a good job of explaining why their approach to surveys makes them a good basis for their data. However, they are still surveys that capture subjective perceptions, and I wonder how their population sample reflects the general IT world. I'll have more confidence in their results when other teams, using different approaches, are able to confirm their reasoning. The book already has some of this, as the work done by Google on team cultures provides further evidence to support their judgment on how important a <strong>Westrum-generative organizational culture</strong> is for effective software teams.</p>

<h3 id="nicoleforsgrenonhowdevopsmetricscorrelatewithorganisationalperformance">Nicole Forsgren On How DevOps Metrics Correlate With Organisational Performance</h3>

<p>Rigorous appraisal of these Four Key Metrics has shown that higher performers are <strong>2x more likely</strong> to meet their <strong>commercial goals</strong> <em>(productivity, profitability, market share, number of customers)</em> and their <strong>non-commercial goals</strong> <em>(quantity of products or services, operating efficiency, customer satisfaction, quality of products or services and achieving organisational or mission goals)</em>. Indeed, companies which do well under these DevOps metrics have a <strong>50%</strong> higher market cap growth over 3 years. 📈</p>

<p><strong>NOTE</strong>: These findings will apply whether you’re using a traditional “waterfall” methodology (also known as <em>gated, structured, or plan-driven</em>) and just beginning your technology transformation, or whether you have been implementing Agile and DevOps practices for years.</p>

<h2 id="flawsinpreviousattemptstomeasureperformance">Flaws in previous attempts to measure performance</h2>

<p>Before I jump into what <em>capabilities</em> a company needs to focus on to achieve these metrics, I wanted to explain how companies have in the past measured performance.</p>

<p>There have been many attempts to measure the performance of software teams. Most of these measurements focus on productivity. In general, they suffer from <strong>two</strong> drawbacks.</p>

<ul>
<li>First, they focus on <strong>outputs rather than outcomes</strong>.</li>
<li>Second, they focus on <strong>individual</strong> or <strong>local measures</strong> rather than a team or global ones.</li>
</ul>

<h3 id="loclinesofcode">LOC (lines of code)</h3>

<p>Measuring productivity in terms of lines of code has a long history in software. Some companies even required developers to record the lines of code committed per week. However, in reality, we would prefer a <em>10-line solution</em> to a <em>1,000-line solution</em> to a problem. Rewarding developers for writing lines of code leads to bloated software that incurs <strong>higher maintenance costs</strong> and <strong>higher costs of change</strong>. </p>

<p>Ideally, we should reward developers for solving business problems with the minimum amount of code and it's even better if we can solve a problem without writing code at all or by deleting code (perhaps by a business process change). However, minimizing lines of code isn't an ideal measure either. At the extreme, this too has its drawbacks. Accomplishing a task in a single line of code that no one else can understand is less desirable than writing a few lines of code that are easily understood and maintained.</p>

<h3 id="velocity">Velocity</h3>

<p>With the advent of Agile software development came a new way to measure productivity: <strong>velocity</strong>. In many schools of Agile, problems are broken down into stories. Stories are then estimated by developers and assigned a number of "points" representing the relative effort expected to complete them. At the end of an iteration, the total number of points signed off by the customer is recorded; this is the team's velocity. </p>

<p>Velocity is designed to be used as a capacity planning tool; for example, it can be used to extrapolate how long it will take the team to complete all the work that has been planned and estimated. However, some managers have also used it as a way to measure team productivity, or even to compare teams.</p>

<p>Using velocity as a productivity metric has several flaws. First, velocity is a relative and team-dependent measure, not an absolute one. Teams usually have significantly different contexts which render their velocities incommensurable. Second, when velocity is used as a productivity measure, teams inevitably work to game their velocity. They inflate their estimates and focus on completing as many stories as at the expense of collaboration with other teams (which might decrease their velocity and increase the other team's velocity, making them look bad). Not only does this destroy the utility of velocity for its intended purpose, but it also inhibits collaboration between teams.</p>

<p>I have seen this in the past myself, <strong>"gaming"</strong> the system and engineers aiming to deliver as my story points as you can within a sprint. Teams celebrating burn down charts and how many points they delivered compared to other squads...</p>

<p>This obfuscates the underlying quality of the code. And what tech debt or improvements you could have made but decided not to. If you don't have the right leadership in place or a peer review process, then this can unfold very quickly. And not being a <a href="https://www.stepsize.com/blog/how-to-be-an-effective-boy-girl-scout-engineer">good girl scout or boy scout</a>. They talk about this more in "system thinking" and not having that <a href="https://medium.com/@Smrimell/it-s-a-trap-systems-traps-in-software-development-dc6341022795#:~:text=you%20actually%20incentivising%3F-,Drift%20to%20low%20performance,-Drift%20to%20low">growth mindset</a>.</p>

<h3 id="utilisation">Utilisation</h3>

<p>Finally, many organizations measure utilization as a proxy for productivity. The problem with this method is that <strong>high utilization</strong> is only good up to a point. Once utilization gets above a certain level, there is no spare capacity (or "slack") to absorb unplanned work, changes to the plan, or improvement work. This results in longer lead times to work. Queue theory in math tells us that as utilization approaches 100%, lead times approach infinity-in other words, once you get to very high levels of utilization, it takes teams exponentially longer to get anything done. Since lead time is a measure of how fast work can be completed, and is a productivity metric that doesn't suffer from the drawbacks of the other metrics we've seen, it's essential that we manage utilization to balance it against lead time in an economically optimal way.</p>

<h2 id="performancemetrics">Performance metrics</h2>

<p>Below you can see the different groupings of teams and how they perform. You can see the elite teams, they're able to deploy on-demand. Their Cycle Time is less than one day. Their MTTR, mean time to restore, is less than one hour. Change failure rate is in the zero to 15% range. You can really see how the Cycle Time, in particular, the lead time for changes, how varies throughout those four columns. It's less than one day for the elite teams. For the high-performance teams, it goes down to that one day and one week. Medium teams one week and one month and the low performing teams are really between that one month and six months mark.</p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-15-at-6-14-09-pm.png" alt="Book review: Accelerate"></p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-15-at-6-08-18-pm.png" alt="Book review: Accelerate"></p>

<p>I will highlight next the main capabilities and provide real use-cases that I have observed in my experience:</p>

<h3 id="capabilities">Capabilities</h3>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Accelerate--1-.jpg" alt="Book review: Accelerate"></p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-15-at-6-23-34-pm.png" alt="Book review: Accelerate"></p>

<h2 id="thoughtsandfeedback">Thoughts and feedback</h2>

<p>As highlighted above, there is a lot of investment needed to achieve CD (Continuous Delivery) and become high-performing. Whenever you are asked about what makes a high-performing team, people say trust and a sense of purpose are important. And they are, don't get me wrong. But you also need engineers with a growth mindset, discipline, and technical acumen to make it a reality.</p>

<p>But regarding trust and a sense of purpose, there are numerous ways to achieve this within your team. Team topologies go in-depth into this discussion. They talk about <em>Dunbar's trust number</em> and having <em>two size pizza teams</em> (normally 5-8 depending on the complexity of the domain). </p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-18-at-11-48-54-am.png" alt="Book review: Accelerate"></p>

<p>Then there is also <em>Tuckman's model approach</em> to team development: <strong>forming-storming-norming-performing</strong>. </p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-16-at-2-35-06-pm.png" alt="Book review: Accelerate"></p>

<p>Regular social events with your team and <a href="https://www.atlassian.com/team-playbook/plays">team playbooks</a> are a great way to break the ice and start to understand eachothers personalities. Especially when you work in a culturally diverse environment. Working in Europe and the Asia Pacific, I have worked with many nationalities and am always keen to understand more about their culture. </p>

<p><strong>Sense of purpose</strong> - having that clear vision and objective for the team and getting alignment is crucial to the success of the team. Running exercises such as <a href="https://miro.com/guides/team-charter/">team charter</a> and <a href="https://miro.com/templates/raci-matrix/">RACI model</a> help with this.</p>

<p>But now onto the fun part; the technical aspects. But I will let you in with a little secret first:</p>

<blockquote>
  <p>Engineers love shipping software! 😛</p>
</blockquote>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-13-at-10-41-01-am.png" alt="Book review: Accelerate"></p>

<p>I remember years ago working in London for a mobile apps company. There was an engineer who I worked with that hadn't shipped code in over six months! (due to indecisiveness on the client's part). I could see how demoralized she looked not able to see her hard work go live. So always remember, engineers, <strong>love to ship software</strong>. </p>

<p>I have worked in high-performing teams in the past where <strong>change lead time</strong> was <strong>&lt; 90 mins</strong> and <strong>deployment frequency daily</strong>. I have seen firsthand how it has a direct impact on job satisfaction.  Having engaged employees doing meaningful work drives <strong>business value</strong>.</p>

<h3 id="deploymentpipeline">Deployment pipeline</h3>

<p>Continuous delivery does not happen overnight. You need to methodically design your CI pipeline, and focus on your technical capabilities to make it a reality. Working with CI servers such as buildkite, GitHub actions or circleCI allows you to design your pipeline as code. We have moved on from the clickops days of TeamCity or bamboo where engineers are not across how CI/CD works. To be able to visualise it as code, can really help even new team members understand and get up to speed so much faster. </p>

<p>A lot of these CI servers now support plugins and integrations to expedite your delivery and simplify the work needed in the pipeline. Accessing secrets from secret manager, running docker-compose, generating test reports, and more. Continually looking to optimise and improve your pipeline to run as fast as possible through certain software techniques/enhancements or agent upgrades is an ongoing task.</p>

<h3 id="testautomation">Test Automation</h3>

<p>Software quality and test automation are the next phases. Working in cross-functional teams can be a mindset change for a lot of engineers. It is well publicised now the mandate at AWS of decoupling architecture and building <em>two size pizza teams</em> that build, run, and support your workloads. Working on teams with <strong>24/7 support</strong>, your mindset changes into how you perceive the quality of your code. Getting a change "out the door" to achieve a deadline is frowned upon. And those traditional measures of <em>velocity</em> and <em>high utilisation</em>.</p>

<p>If you are on 24/7 support, you don't want to be woken up at 3 AM in the morning right! So your mindset changes. You are more aware of the quality of your code and of others. Working on trunk-based development makes engineers' life also easier when reviewing PRs.</p>

<p>There is a lot of talk about shifting left and specifically shifting testing left. Having a healthy automated test suite and being aware of anti-patterns such as ice cream cones and <a href="https://www.thoughtworks.com/en-au/insights/blog/introducing-software-testing-cupcake-anti-pattern">cupcake</a> is very important. </p>

<p>You cannot achieve CD by having a <strong>heavy</strong> layer of e-2-e tests. This will delay your change lead time and overtime those tests will become <a href="https://www.thoughtworks.com/en-au/insights/blog/transitioning-conventional-shift-left-testing">flaky</a> and less reliable. </p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/ice_cream_cone.png" alt="Book review: Accelerate"></p>

<p>Shifting left to integration tests (that can run on your local machine) is the key. QAs focus less on manual tests and free them up to look at exploratory testing or chaos engineering for example. But many high-performing teams these days hire more engineers to focus on test automation and shifting testing and performance left rather than hire QAs. Working with the teams to educate them on the benefits of shifting left and changing that mindset of how to test a feature is extremely valuable.</p>

<p>Now companies are given the space to revisit their environments and look to <a href="https://www.infoq.com/news/2022/05/removing-staging-environments/">consolidate</a>. Not only does this improve change lead time and MTTR. But also the maintenance overhead of trying to keep envs in <a href="https://12factor.net/dev-prod-parity">parity</a>. Cost savings on your cloud provider can be significant too.</p>

<h3 id="featuretoggles">Feature toggles</h3>

<p>Feature toggles are a great way to separate deploy from release when looking to consolidate envs. There are several commercial options out there such as launch darkly and other <a href="https://github.com/Unleash/unleash">open source</a> options to help with this. <a href="https://github.blog/2021-04-27-ship-code-faster-safer-feature-flags/">Github</a> provides a great example of how they ship frequently and safely to production with the use of feature toggles.</p>

<h3 id="cloud">Cloud</h3>

<p>Cloud infrastructure is adopted quite heavily now by companies. And there is an expectation now for engineers to upskill in cloud providers such as AWS or Google Cloud. Working in cross-functional teams in the past we managed our own AWS accounts and infrastructure. There are many tools of choice for infra as code such as cloudformation, terraform, AWS CDK, and pulumi. </p>

<p>Companies also these days are identifying areas of the platform to design and build to provide self-service capabilities to the delivery teams. I have worked with teams that treated the platform as a product and its internal customers as delivery teams and looked to streamline their development process by providing custom tooling with well-documented APIs and support. </p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-16-at-5-57-35-pm.png" alt="Book review: Accelerate"></p>

<p>Deploying static sites and deploying app workloads to Kubernetes are examples I have seen. The key is to balance <strong>autonomy with alignment</strong> and still allow teams to choose their own tooling when needed. They call it providing a "thinnable viable" platform.</p>

<p>Slack has a great article on how they adopted this <a href="https://slack.engineering/applying-product-thinking-to-slacks-internal-compute-platform/">approach</a>.</p>

<h3 id="proactivemonitoring">Proactive monitoring</h3>

<p>Once you are happy with your pipeline, code quality, and test automation, the next area to invest in is monitoring. I am not going into the specifics here but you have many tools at your disposal now for logging and monitoring namely Splunk, sumologic, datadog, new relic, and appdymanics. </p>

<p>In a loosely coupled architecture having distributed tracing and a common log format really helps when troubleshooting. Investing time in an incident management playbook and alert notifications too. <mark>Just be aware of <a href="https://www.pagerduty.com/blog/reduce-alert-fatigue/!">alert fatigue</a></mark></p>

<h3 id="leanmanagementandproductdevelopment">Lean Management and product development</h3>

<p>With this investment in CD, your team can now afford to streamline its change approval process. Gone are the days that you need to present to CAB for production deployments. This allows the team to adopt a peer-review process via pair programming or intra-team code review without sacrificing speed or quality. </p>

<p>One common requirement I hear from companies is segregation of duties which is a common regulatory requirement. This can now be achieved by using peer review to meet the goal of segregation of duties, with <strong>reviews</strong>, <strong>comments</strong>, and <strong>approvals</strong> captured in the team's development platform as part of the development process.</p>

<p>The time and attention of those in leadership and management positions are freed up to focus on more strategic work. This transition, from <em>gatekeeper</em> to <em>process architect and information beacon</em>, is consistent with the practices of organizations that excel at software delivery performance.</p>

<blockquote>
  <p>CAB idea is a form of risk management theater: we check boxes so that when something goes wrong, we can say at least we followed the process. At best, the process only introduces time delays and handoffs.</p>
</blockquote>

<p>Lean product management and visibility of work from the business all the way through to customers is a new approach that I have seen. Team experimentation, visibility into customer feedback, and <a href="https://www.producttalk.org/continuous-discovery/">continous discovery</a> are ways for the team and engineers to feel empowered to contribute on what opportunities to work on next, and become more engaged in product discovery and engaging with customers.</p>

<h3 id="shiftingsecurityleft">Shifting security left</h3>

<p>Security is another CFR that can be shifted left. Areas I have worked on in the past are the automated management of dependencies via tools such as <a href="https://github.blog/2020-06-01-keep-all-your-packages-up-to-date-with-dependabot/">dependabot</a>, <a href="https://snyk.io/">snyk</a> or <a href="https://www.whitesourcesoftware.com/free-developer-tools/renovate/">renovate</a>. </p>

<p>We devised a schedule on what frequency and priority we needed to be notified of, for our repos to prevent dependency fatigue. Having multiple automated PRs created every day for patches is not an efficient use of time! Tools such as snyk also provide infra scanning and container scanning. Engaging teams such as architecture and security earlier in the development lifecycle is also recommended.</p>

<h3 id="cultureandtransformativeleadership">Culture and transformative leadership</h3>

<p>And lastly, but most importantly <strong>culture</strong>. Creating a climate for learning is a key investment within a company and investing in your people. <br>
Some steps that I have advocated for and adopted in the past include:</p>

<ul>
<li>Creating a training budget</li>
<li>Ensuring the team has the resources to engage in informal learning and space to explore ideas</li>
<li>Blameless culture and making it safe to fail</li>
<li>Creating opportunities and spaces to share information through lightning talks or guilds</li>
<li>Make resources available for continued education such as meetups and attending conferences</li>
</ul>

<p>As this survey has highlighted, investing in culture that values <em>learning</em> contributes significantly to software delivery performance.</p>

<p>As a leader, it is important to really invest in culture and be able to measure it. Your work is never done, always looking to continually improve. Providing the <strong>technical vision</strong> and <strong>where the company is going</strong>, <strong>supportive leadership</strong>, and <strong>challenging team members</strong> (intellectual stimulation) are the areas I continually focus on.</p>

<h2 id="pitfallstowatchoutfor">Pitfalls to watch out for</h2>

<ul>
<li><p><strong>Focus on capabilities and not maturity</strong> The key to successful change is measuring and understanding the right things with a focus on capabilities - not on maturity.
Maturity models focus on helping an org <code>"arrive"</code> at a mature state and declare themselves done with their journey. Alternatively, capability models focus on helping an org continually improve and progress, realising that the technological and business landscape is ever-changing.</p></li>
<li><p><strong>Treating transformation as a one-time project.</strong> in high-performing organizations, getting better is an ongoing effort and part of everybody's daily work. However, many transformation programs are treated as large-scale, one-time events in which everyone is expected to rapidly change the way they work and then continue on with business as usual. Teams are not given the capacity, resources, or authority to improve the way they work, and their performance gradually degrades as the team's processes, skills, and capabilities become an ever poorer fit for the evolving reality of the work. You should think of technology transformation as an important value-delivery part of the business, one that you won't stop investing in. After all, do you plan to stop investing in customer acquisition or customer support?</p></li>
<li><p><strong>Treating transformation as a top-down effort.</strong> In this model, organizational reporting lines are changed, teams are moved around or restructured, and new processes are implemented. Often, the people who are affected are given little control of the changes and are not given opportunities for input. This can cause stress and lost productivity as people learn new ways of working, often while they are still delivering on existing commitments. When combined with the poor communication that is frequent in transformation initiatives, the top-down approach can lead to employees becoming unhappy and disengaged. It's also uncommon for the team that's planning and executing the top-down transformation to gather feedback on the effects of their work and to make changes accordingly. Instead, the plan is implemented regardless of the consequences.</p></li>
</ul>

<h2 id="googleproject">Google project</h2>

<p>Google provides a very useful project on <a href="https://github.com/GoogleCloudPlatform/fourkeys">github</a> showcasing how to capture these metrics. They use services on GCP such as Pub/Sub and BigQuery to capture this data in real-time. I would love to see an example of using Kafka and ksqlDB to mirror this approach (hack day idea!)</p>

<p><img src="https://raw.githubusercontent.com/GoogleCloudPlatform/fourkeys/main/images/dashboard.png" alt="Book review: Accelerate"></p>

<p>To find out where your company sits, run this <a href="https://www.devops-research.com/quickcheck.html">quick survey</a></p>

<p>I hope you have found this post insightful and helped create a conversation in your organization about where you stand today. And where you want to go.</p>

<p>As my main man Kelsey said:</p>

<p><img src="https://blog.shanelee.name/content/images/2022/05/Screen-Shot-2022-05-15-at-5-38-24-pm.png" alt="Book review: Accelerate"></p>]]></content:encoded></item><item><title><![CDATA[Performance testing with k6]]></title><description><![CDATA[<p>I started looking into API performance testing again recently. As part of delivering an API to production, performance testing is crucial to see how your API performs under load ( more on that shortly)</p>

<p>You might have heard of the ridiculous load that Shopify handled over <strong>BFCM</strong> (Black Friday/Cyber Monday)</p>]]></description><link>https://blog.shanelee.name/2021/12/15/performance-testing-with-k6/</link><guid isPermaLink="false">35b5397d-d54f-4305-9f95-32975623f93f</guid><category><![CDATA[testing]]></category><category><![CDATA[oas]]></category><category><![CDATA[grafana]]></category><category><![CDATA[docker]]></category><category><![CDATA[api]]></category><category><![CDATA[k6]]></category><category><![CDATA[perf]]></category><category><![CDATA[apifirst]]></category><category><![CDATA[typescript]]></category><category><![CDATA[performance]]></category><category><![CDATA[api first]]></category><category><![CDATA[javascript]]></category><category><![CDATA[golang]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Wed, 15 Dec 2021 06:03:00 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2021/12/marc-olivier-jodoin-NqOInJ-ttqM-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2021/12/marc-olivier-jodoin-NqOInJ-ttqM-unsplash.jpg" alt="Performance testing with k6"><p>I started looking into API performance testing again recently. As part of delivering an API to production, performance testing is crucial to see how your API performs under load ( more on that shortly)</p>

<p>You might have heard of the ridiculous load that Shopify handled over <strong>BFCM</strong> (Black Friday/Cyber Monday) weekend. They executed and ran performance testing on their systems well in advance as far back as July to make sure they were well prepared for <code>"game-day"</code>.</p>

<p><blockquote class="twitter-tweet"><p lang="en" dir="ltr">⏪ Rewinding for a moment — preparing for an event of this scale doesn&#39;t happen overnight. In anticipation of BFCM 2021 we began load testing back in July! To better simulate real global traffic we spread out our load generation across <a href="https://twitter.com/googlecloud?ref_src=twsrc%5Etfw">@GoogleCloud</a>&#39;s global network. <a href="https://t.co/5oXqFOadae">pic.twitter.com/5oXqFOadae</a></p>&mdash; Shopify Engineering (@ShopifyEng) <a href="https://twitter.com/ShopifyEng/status/1465806698954772489?ref_src=twsrc%5Etfw">November 30, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p>

<p>Now there are many tools that are out there in the wild. I have worked with <code>JMeter</code> quite extensively, <code>Gatling</code> and <code>Artillery</code>. But as always, I like to do some research and see what is happening in that space.</p>

<p>Enter <code>k6</code>!</p>

<p>I follow ThoughtWorks <a href="https://www.thoughtworks.com/radar/tools?blipid=202010078">tech radar</a> and they identified this new tool. So I decided to have a play around.</p>

<h2 id="k6">K6</h2>

<p><img src="https://raw.githubusercontent.com/grafana/k6/master/assets/logo.svg" alt="Performance testing with k6"></p>

<ul>
<li>Created in 2016 by loadImpact</li>
<li>Acquired by Grafana in 2021</li>
<li>14.6k Github stars ⭐️</li>
<li>Built in golang, test scripts using <del>Javascript</del> Typescript 😉</li>
<li>Multiple choices for storage (datadog/kafka/cloudwatch/Prometheus/JSON/CSV)</li>
<li>Converters (HAR/Postman/OAS)</li>
<li>Github action</li>
<li>Test builder/browser recorder (low code options)</li>
<li>Aligns with “API first” approach</li>
</ul>

<p><mark>I mention "API first" here as that is a common practice for organizations now.</mark> In my previous post, I talked about another API first tool to use when <a href="https://blog.shanelee.name/2021/08/29/mocking-a-rest-api-the-api-first-way-with-mockoon/">mocking APIs</a>.</p>

<h3 id="installation">Installation</h3>

<p>There are several ways to install k6 depending on your operating system or system environment. But in this article, I will only touch on two of them, MacBook and Docker.</p>

<p>You can install k6 on a MacBook by using brew</p>

<pre><code class="language-bash">brew install k6
</code></pre>

<p>or via docker</p>

<pre><code class="language-bash">docker pull loadimpact/k6  
</code></pre>

<p>You can find the full explanation here <a href="https://k6.io/docs/getting-started/installation/">https://k6.io/docs/getting-started/installation/</a></p>

<h3 id="typescripttemplate">Typescript template</h3>

<p>As I mentioned above, the test scripts can be defined in javascript. But I have decided from the beginning to use typescript instead for static type checking. I have created a typescript template on <a href="https://github.com/shavo007/k6-demo">Github</a> that can be easily re-used by anyone. It is built on the good work that k6 did with some enhancements.</p>

<p><img src="https://blog.shanelee.name/content/images/2021/12/Screen-Shot-2021-12-16-at-5-23-12-pm.png" alt="Performance testing with k6"></p>

<h4 id="rationale">Rationale</h4>

<p>While JavaScript is great for a myriad of reasons, one area where it falls short is type safety and developer ergonomics. It's perfectly possible to write JavaScript code that will look <code>OK</code> and behave <code>OK</code> until a certain condition forces the executor into a faulty branch.</p>

<p>While it, of course, still is possible to shoot yourself in the foot with TypeScript as well, it's significantly harder. Without adding much overhead, TypeScript will:</p>

<ul>
<li>Improve the ability to safely refactor your code.</li>
<li>Improve readability and maintainability.</li>
<li>Allow you to drop a lot of the defensive code previously needed to make sure consumers are calling functions properly.</li>
</ul>

<h3 id="lifecycle">Lifecycle</h3>

<p>The four distinct life cycle stages in a k6 test are <code>"init"</code>, <code>"setup"</code>, <code>"VU"</code> and <code>"teardown"</code></p>

<h4 id="initandvustages">Init and VU stages</h4>

<p>Scripts must contain, at the very least, a default function - this defines the entry point for your VUs, similar to the <code>main()</code> function in many other languages.</p>

<p>Code inside default is called "VU code", and is run over and over for as long as the test is running. Code outside of it is called "init code", and is run only once per VU (Virtual User).</p>

<p>A VU will execute the default function from start to end in sequence. Nothing out of the ordinary so far, but here's the important part; once the VU reaches the end of the default function it will loop back to the start and execute the code all over.</p>

<h4 id="setupandteardownstages">Setup and teardown stages</h4>

<p>Beyond the required <code>init</code> and <code>VU</code> stages, which is code run for each VU, k6 also supports test-wide setup and teardown stages, like many other testing frameworks and tools. The setup and teardown functions, like the default function, need to be exported functions. But unlike the default function setup and teardown are only called once for a test. setup is called at the beginning of the test, after the init stage but before the VU stage (default function), and teardown is called at the end of a test, after the VU stage (default function).</p>

<p>You might have noticed the function signature of the default function and teardown function takes an argument, which we here refer to as data.</p>

<p>This data will be whatever is returned in the setup function, so a mechanism for passing data from the setup stage to the subsequent VU and teardown stages.</p>

<p><em>Further below I will showcase how in <code>setup</code> you can inject in an access token to the default function for an API secured by OAuth2</em>.</p>

<h3 id="basicexample">Basic example</h3>

<pre><code class="language-javascript">import { sleep, check } from "k6";  
import { Options } from "k6/options";  
import http, { Response } from "k6/http";  
import { padStart } from "lodash";  
import { textSummary } from "./helper";  
import { Trend } from "k6/metrics";

// 1. init code

console.log(padStart("Hello TypeScript!", 20, " "));

//custom define metric
const durationInSeconds = new Trend("duration_in_seconds");

export let options: Options = {  
  vus: 5, //no. of concurrent virtual users
  duration: "5s",
  discardResponseBodies: true, //discard response bodies to improve perf
  //if you want to fail the whole load test use thresholds
  thresholds: {
    http_req_failed: ["rate&lt;0.01"], // http errors should be less than 1%
    http_req_duration: ["p(95)&lt;350"], // 95% of requests should be below 350ms
  },
  // httpDebug: "true",
  userAgent: "K6GreetingsDemo/1.0",
};

export function setup() {  
  //setup is called once off
  // 2. setup code
}

export default () =&gt; {  
  // 3. VU code

  let baseUrl = __ENV.BASE_URL ?? "http://localhost:8090";
  let url = `${baseUrl}/greetings`;
  const res: Response = http.get(url, {
    tags: { team: "team-label", api: "greetings" },
  });
  check(res, {
    "status is 200": () =&gt; res.status === 200
  });
  // we know that the duration is in millisecond
  // but for demonstration purposes, we convert it to second
  durationInSeconds.add(res.timings.duration / 1000);
  sleep(1);
};

export function handleSummary(data: any) {  
  console.log("Preparing the end-of-test summary...");

  // Send the results to some remote server or trigger a hook

  return {
    stdout: textSummary(data, { indent: " ", enableColors: true }), // Show the text summary to stdout...
  };
}
</code></pre>

<p>First, we imported the dependencies on top. k6 has types (<code>@k6/types</code>) that are nice when developing in typescript. </p>

<p><em>Note that in the background, k6 doesn’t run in NodeJS, since in general JavaScript is not well suited for high performance. It’s written in <code>Go</code> to achieve the desired high-performance testing.</em></p>

<p>The test itself is running inside the exported <code>default</code> function. This part of the code is what we usually called as <code>VU Code</code>. So, the test is running once and uses only one virtual user (think of this as a real user, but simulated) by default, but you can change that using options. <em>We discussed the lifecycle earlier</em>.</p>

<p>So in the example above, we are simulating 5 users over 5 secs. We have set thresholds for the test in relation to response time and error rate. We have added in an additional <code>trend</code> to showcase how that is defined. No setup is involved here. We have one check that the status is <code>200</code>.</p>

<p>To run this we can run the script <code>yarn bundle</code>. This will transpile to JS using babel and bundled with webpack. The outputted file will be located in the <code>dist</code> dir. <br>
Now run locally:</p>

<pre><code class="language-bash">k6 run dist/greetings.js  
</code></pre>

<p>You’ll see the result of the test right away on the terminal. Something similar to this.</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/greeting-test-results.png" alt="Performance testing with k6"></p>

<p>You can see the built-in metrics that were outputted and also your custom metrics <code>duration_in_seconds</code>. To read more about the metrics go to <a href="https://k6.io/docs/using-k6/metrics/">https://k6.io/docs/using-k6/metrics/</a></p>

<p>Now I have only outputted this to stdout. But you may decide you want to pipe it into a time series database like influxdb, APM such as datadog or appdynamics which is all possible. For more on the supported outputs check out <a href="https://k6.io/docs/getting-started/results-output/">https://k6.io/docs/getting-started/results-output/</a>. If you want to look at an example of using influxdb and grafana check out <a href="https://github.com/shavo007/k6-demo#load-testing-with-influxdb-and-grafana">here</a>.</p>

<h3 id="apifirsttools">API first tools</h3>

<p>Now I mentioned earlier that it supports the "api first" approach. In the past, I have looked to use tools such as open api generator to generate server stubs based on the OAS. </p>

<p><em><a href="https://slack.engineering/how-we-design-our-apis-at-slack/">Slack</a> have talked about this approach recently regarding their APIs. And <a href="https://blog.stoplight.io/api-first-vs.-api-design-first-a-comprehensive-guide">stoplight</a> has some great resources on the API first approach too.</em> So I looked into open api generator and saw they actually supported k6. Let's try that out next.</p>

<h4 id="oask6">OAS + k6</h4>

<p>I have designed a sample <a href="https://github.com/shavo007/k6-demo/blob/main/oas3.yaml">OAS</a> already, called <code>greetings API</code> and I wanted to generate the k6 script using open api generator. You can follow along by cloning this <a href="https://github.com/shavo007/k6-demo">repo</a></p>

<pre><code class="language-bash">docker pull openapitools/openapi-generator-cli

docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate \  
    -i /local/oas3.yaml \
    -g k6 \
    -o /local/k6-oas3/ \
    --skip-validate-spec
</code></pre>

<p>Here I pull down the docker image and then run the cmd by mounting my oas file and using the generator type <code>k6</code>. This then generates some boilerplate test script code inside the dir <code>k6-oas3</code>.</p>

<p><mark><strong>NB:</strong> This will need to be refined and enhanced afterward but is a great starting point</mark>.</p>

<h4 id="openapitotypescript">open api to typescript</h4>

<p>Another tool I found useful is this node module <a href="https://www.npmjs.com/package/openapi-typescript">open api typescript</a>. </p>

<p>I read an article recently on how <a href="https://codeascraft.com/2021/11/08/etsys-journey-to-typescript/">etsy</a> migrated from JS to TS and they used this tool to generate the types which can save a lot of time and aligns with the "API first" approach.</p>

<p>So in the example below I will showcase how to use the types when running a load test against <a href="https://developer.bpaygroup.com.au/validate-bpay-payment">bpay API</a>. This API is secured by <code>OAuth2 client credentials</code> grant type also. You will see that I use the <code>setup</code> function to inject in the access_token that's needed when running the test.</p>

<p>As I am defining my script in typescript and this API method is a post, I can use <code>openapi-typescript</code> to generate the types for me from the OAS. This saves me a lot of time not having to define my own interfaces.</p>

<p>To generate the types:</p>

<pre><code class="language-bash">npx openapi-typescript bpay/oas3.yaml --output src/bpay/schema.ts  
</code></pre>

<p>This generates the types in the <code>bpay</code> dir.</p>

<p>Now let's have a look at the test script for bpay API.</p>

<pre><code class="language-javascript">import http from "k6/http";  
import { group, check, sleep } from "k6";  
import { PaymentPaymentMethodEnum } from "./bpay";  
import { Options } from "k6/options";  
import { getToken, Options as BpayOptions } from "./helper";  
import { components } from "./bpay";

type Payment = components["schemas"]["Payment"];  
type PaymentItem = components["schemas"]["PaymentItem"];

const BASE_URL = "https://sandbox.api.bpaygroup.com.au/payments/v1";  
// Sleep duration between successive requests.
// You might want to edit the value of this variable or remove calls to the sleep function on the script.
const SLEEP_DURATION = 0.1;  
// Global variables should be initialized.

export let options: Options = {  
  vus: 1, //no. of concurrent visual users
  duration: "1s",
  httpDebug: "true",
};

export function setup() {  
  const options: BpayOptions = {
    clientId: `${__ENV.CLIENT_ID}`,
    clientSecret: `${__ENV.CLIENT_SECRET}`,
    domain: "https://sandbox.api.bpaygroup.com.au",
  };
  try {
    return getToken(options);
  } catch (error) {
    return "";
  }
}

//desctructing assigment here and explicit type annotation
export default function ({ access_token = "" }: { access_token: string }) {  
  group("/validatepayments", () =&gt; {
    let url = BASE_URL + `/validatepayments`;

    const payment: Payment = {
      billerCode: "565572",
      crn: "651234567890123",
      amount: 234.83,
      paymentMethod: PaymentPaymentMethodEnum.Debit,
      settlementDate: "2017-10-23",
    };
    const payment1: Payment = {
      billerCode: "1313",
      crn: "1230",
      amount: 1045.98,
      paymentMethod: PaymentPaymentMethodEnum.Debit,
      settlementDate: "2017-11-06",
    };
    const paymentItem: PaymentItem = { tid: "1", payment };
    const paymentItem1: PaymentItem = { tid: "2", payment: payment1 };
    const payments: Array&lt;PaymentItem&gt; = [];
    payments.push(paymentItem);
    payments.push(paymentItem1);
    const payload = { payments };
    let params = {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
        Authorization: `Bearer ${access_token}`,
      },
    };
    let request = http.post(url, JSON.stringify(payload), params);
    check(request, {
      "ok":
        (r) =&gt; r.status === 200,
    });
    sleep(SLEEP_DURATION);
  });
}
</code></pre>

<p>Let's look at the imports first. You can see we are importing bpay types that were auto-generated. These types are subsequently used when building up the payload inside the <br>
default function. I'm running against bpays sandbox env and it's a simple <a href="https://k6.io/docs/test-types/smoke-testing/">smoke test</a>. </p>

<p><em>I will shed some light on the different types of performance tests you can run later</em>.</p>

<p>Now in the setup function, you can see I've defined a helper fn to get the access token. This performs a request against the sandbox env with your client id and secret and returns the access token. I can then return this value which is then passed into the default fn to be used in the Authorisation header.</p>

<p><mark><strong>NB</strong> I am using env variables here for client_id and secret as they should not be committed into src code</mark>.</p>

<p>The default fn constructs the payload and submits the post request to the API with the bearer token.</p>

<p>I have not defined any thresholds in this example but just a simple check on the HTTP status response. <br>
You can see how easy it is to define your smoke test and utilize the tools at your disposal if you adopt the "api first" approach.</p>

<h3 id="browserbuilder">Browser builder</h3>

<p>Now, let's say I'm a bit lazy 😅 and want to go the low-code approach! k6 does have a <a href="https://k6.io/docs/test-authoring/recording-a-session/">browser recorder extension</a> for chrome and firefox that can record your interactions and download a har (HTTP archive file). This then can be imported into <a href="https://k6.io/docs/test-authoring/test-builder/">k6 cloud</a> and provide a nice GUI to construct the script. Once you are happy with the script you can copy this locally and use it with the CLI.</p>

<p>Builder:</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/builder.png" alt="Performance testing with k6"></p>

<p>Corresponding script:</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/app-k6-io-Performance-testing-for-developers-like-unit-testing-for-performance.png" alt="Performance testing with k6"></p>

<h3 id="typesoftesting">Types of testing</h3>

<p>The examples above showcased smoke tests running against your environment(s) (or what is now called "shifting perf left"). But it is possible to perform many types of tests using k6, each type serving a different purpose.</p>

<h4 id="smoketest">Smoke test</h4>

<blockquote>
  <p>Smoke test is a regular load test, configured for a minimal load.</p>
</blockquote>

<p>You want to run a smoke test as a <code>sanity check</code> every time you write a new script or modify an existing script.</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/smoke-test.png" alt="Performance testing with k6"></p>

<ul>
<li>can be run as part  of CI pipeline - "shifting perf left"</li>
<li>More ideal for microservices</li>
</ul>

<p>k6 options for smoke test:</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/smoke.png" alt="Performance testing with k6"></p>

<h4 id="loadtest">Load test</h4>

<blockquote>
  <p>Load Testing is primarily concerned with assessing the current performance of your system in terms of concurrent users or requests per second.</p>
</blockquote>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/load-test.png" alt="Performance testing with k6"></p>

<p>If you need some understanding about the amount of traffic your system is seeing on average and during peak <br>
hours. <strong>ie.</strong> How to configure performance thresholds.</p>

<p><mark>If your system crashes under a load test, it means that
your load test has morphed into a stress test!</mark></p>

<p>K6 options for load test:</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/load.png" alt="Performance testing with k6"></p>

<h4 id="stresstest">Stress test</h4>

<blockquote>
  <p>Stress testing is to assess the availability and stability of the system under heavy load (think HA).</p>
</blockquote>

<p>To execute a proper stress test, you need a tool to push the system over its normal operations, to its limits, and beyond the <em>breaking point</em>.”</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/stress-test.png" alt="Performance testing with k6"></p>

<p>You typically want to stress test an API or website to <br>
determine:</p>

<ul>
<li>How your system will behave under extreme
conditions.  </li>
<li>What the maximum capacity of your system is in
terms of users or throughput.  </li>
<li>The breaking point of your system and its failure
mode.  </li>
<li>If your system will recover without manual
intervention after the stress test is over.</li>
</ul>

<p><em>Companies use this form of testing for <strong>black Friday sales</strong> for example</em>.</p>

<p>K6 options for stress test:</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/stress.png" alt="Performance testing with k6"></p>

<h4 id="soaktest">Soak test</h4>

<blockquote>
  <p>A soak test uncovers performance and reliability issues stemming from a system being under
  pressure for an extended period.</p>
</blockquote>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/soak-test.png" alt="Performance testing with k6"></p>

<p>You typically run this test to:</p>

<ul>
<li>Verify that your system doesn't suffer from bugs or
memory leaks, which result in a crash or restart after <br>
several hours of operation.  </li>
<li>Verify that expected application restarts don't lose
requests.  </li>
<li>Find bugs related to race-conditions that appear
sporadically.  </li>
<li>Make sure your database doesn't exhaust the
allotted storage space and stops.  </li>
<li>Make sure your logs don't exhaust the allotted disk
storage.  </li>
<li>Make sure the external services you depend on don't
stop working after a certain amount of requests are <br>
executed.</li>
</ul>

<p>K6 options for soak test:</p>

<p><img src="https://raw.githubusercontent.com/shavo007/k6-demo/main/assets/soak.png" alt="Performance testing with k6"></p>

<h3 id="conclusion">Conclusion</h3>

<p>If you are comfortable developing in javascript or typescript, k6 is a breeze to use. There are so many options you could use, scenarios if you need advanced user behavior, saving the test result in a CSV or JSON file, having a dashboard for presentation using grafana, etc.</p>

<p>As I demoed earlier, if you adopt the "API first" approach there are tools you can use such as <code>open API generator</code> and <code>openapi-to-typescript</code> that can help improve the development experience.</p>

<p>Happy testing! </p>]]></content:encoded></item><item><title><![CDATA[Mocking a REST API the "API first" approach with Mockoon]]></title><description><![CDATA[How to mock an API with mockoon. Showcase using open API spec and mockoon to mock an API and run with docker.]]></description><link>https://blog.shanelee.name/2021/08/29/mocking-a-rest-api-the-api-first-way-with-mockoon/</link><guid isPermaLink="false">a64b3164-634b-4cd1-8761-2af6a3c1c2c1</guid><category><![CDATA[swagger]]></category><category><![CDATA[docker]]></category><category><![CDATA[openapi]]></category><category><![CDATA[oas]]></category><category><![CDATA[github]]></category><category><![CDATA[api]]></category><category><![CDATA[rest]]></category><category><![CDATA[mock]]></category><category><![CDATA[stub]]></category><category><![CDATA[mockoon]]></category><category><![CDATA[insomnia]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Sun, 29 Aug 2021 07:27:00 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2021/08/chris-ensminger-gWo-hfRotrI-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2021/08/chris-ensminger-gWo-hfRotrI-unsplash.jpg" alt="Mocking a REST API the "API first" approach with Mockoon"><p>Recently, I have spent considerable time researching and analyzing the tooling available for "API first". At the core of this eco-system is the OAS (Open API specification) or interface as we normally like to call it. </p>

<h2 id="oasopenapispecification">OAS (Open API Specification)</h2>

<p>OAS (or what was commonly known as swagger spec) is the industry standard for defining REST interfaces. I had previously worked with many others such as RAML and Blueprint, but it's good to see we have a winner! <a href="https://github.com/OAI/OpenAPI-Specification/releases/tag/3.1.0">Latest</a> release of OAS (V3.x) includes webhook support and the latest JSON schema draft. Open API is now under the governance of the Linux Foundation. The OpenAPI Specification was originally based on the Swagger Specification, donated by SmartBear Software.</p>

<h2 id="mocking">Mocking</h2>

<p>The term <strong>"mock"</strong> for a lot of developers will have unit-testing connotations. In unit-testing, a mock is a fake implementation of a class or function, which accepts the same arguments as the real thing. It might return something pretty similar to the expected output, and different test cases might even modify those returns to see how the code under test works.</p>

<p>This is almost exactly the concept here, just at a HTTP level instead. This is done using a "mock server", which will respond to the expected endpoints, error for non-existent endpoints, often even provide realistic validation errors if a client sends it an invalid request.</p>

<p>So today, I am going to talk about mocking REST APIs. Anyone that has worked in cross-functional teams before would be very used to mocking APIs for local development. If for example, your team consisted of front and back-end devs, normally the BE devs would aim to design the interface upfront (API first) and provide a mock API to FE devs to commence development in parallel. </p>

<p>Why? <br>
Both streams of work should occur in parallel, rather than sequentially. Less waterfall!</p>

<p>But there are many other benefits of mocking APIs such as:</p>

<ul>
<li>Showcasing to stakeholders the interactions as part of the API design process</li>
<li>Showcase to external consumers</li>
<li>Use as a sandbox on the dev portal</li>
<li>Integration testing on CI ("shift testing left")</li>
<li>Performance testing on CI ("shift perf left")</li>
</ul>

<h3 id="tools">Tools</h3>

<p>I have used many frameworks and tools in the past to mock APIs. When I developed with typescript or nodeJS, a framework I used heavily was expressJS. Other tools out there include mountebank or wiremock. But now there is a new breed of mock API tools that are <strong>OAS compliant</strong>. Two that I have found recently are prism from stoplight and Mockoon.</p>

<h2 id="mockoon">Mockoon</h2>

<p><img src="https://blog.shanelee.name/content/images/2021/08/Mockoon.svg" alt="Mocking a REST API the "API first" approach with Mockoon"></p>

<blockquote>
  <p>Mockoon lets you mock an API in seconds</p>
</blockquote>

<p>Some features include:</p>

<ul>
<li>Intuitive interface to create your mock API and run anywhere via CLI</li>
<li>Integrates with your workflow - Compatible with the OpenAPI specification, Mockoon integrates perfectly with your existing applications and API design workflow.</li>
<li>Advanced features and tackle the most complex situation with HTTP requests recording, proxying, integration testing, etc.</li>
<li>Complex rules system and dynamic body templating</li>
<li>Powerful forwarding and debugging</li>
</ul>

<p><insert img="" here=""></insert></p>

<h2 id="creatingourfirstapiwithmockoon">Creating our first API with Mockoon</h2>

<h3 id="step1installation">Step1. Installation</h3>

<p>Mockoon is available on the three major operating systems: Windows, macOS, and Linux. <br>
You can install the native app <a href="https://mockoon.com/download/">here</a>.</p>

<h3 id="step2createyourfirstmockapi">Step 2. Create your first mock API</h3>

<p>After launching the application for the first time, you will find a demo mock API, also called "environment" in Mockoon. You can keep it and build from here or create a new one. To create a new mock API, open the collapsible environments menu on the left and press the blue "plus" button:</p>

<p><img src="https://blog.shanelee.name/content/images/2021/08/Screen-Shot-2021-08-27-at-11-42-09-am.png" alt="Mocking a REST API the "API first" approach with Mockoon"></p>

<h3 id="step3createyourfirstapiroute">Step 3. Create your first API route</h3>

<p>The newly created mock API already includes a route on <code>/</code>. You can modify it by setting up the method and path of your choice.</p>

<p>You can also create a new endpoint by clicking on the blue "plus" button at the top of the endpoint list:</p>

<p><img src="https://blog.shanelee.name/content/images/2021/08/Screen-Shot-2021-08-27-at-11-55-50-am-5.png" alt="Mocking a REST API the "API first" approach with Mockoon"></p>

<h3 id="step4apiendpointconfiguration">Step 4. API endpoint configuration</h3>

<p>You can further customize your endpoint by adding a custom header and the following sample body (which makes use of Mockoon's templating system).</p>

<h3 id="step5runandcallmockapi">Step 5. Run and call Mock API</h3>

<p>The last step is to run your mock API. For this, click on the green "play" arrow in the header:</p>

<p>Your mock server is now available on <code>http://localhost:3001</code> (but also on <code>http://127.0.0.1</code> and all your local network adapters).</p>

<p>You can do a test call to the following URL <code>http://localhost:3001/tutorials</code> using your favorite tool (here using <a href="https://insomnia.rest/">Insomnia</a>) and see the returned response:</p>

<p><img src="https://blog.shanelee.name/content/images/2021/08/Screen-Shot-2021-08-27-at-11-59-39-am-3.png" alt="Mocking a REST API the "API first" approach with Mockoon"></p>

<pre><code class="language-bash">curl --request POST \  
  --url http://localhost:3001/tutorials
</code></pre>

<p>So now you have got the hang of it, let's look at its more advanced features and its "API first" support.</p>

<h2 id="apifirstapproach">API first approach</h2>

<p>So let's import a sample OAS file with Mockoon.</p>

<h3 id="step1importoas">Step 1 Import OAS</h3>

<p>Open the app and go to <code>Import/Export &gt; Swagger/Open API &gt; Import Swagger v2/Open API v3</code></p>

<p>I have defined a sample OAS file <a href="https://github.com/shavo007/mockoon-demo/blob/main/oas3.yaml">here</a> that you can use to follow along. Once you import, it now should look like this.</p>

<p><img src="https://blog.shanelee.name/content/images/2021/08/Screen-Shot-2021-08-27-at-12-22-57-pm.png" alt="Mocking a REST API the "API first" approach with Mockoon"></p>

<p>You can see that for each route, it has already been set up for you all the responses you have defined in the spec and the examples. You can easily toggle on random responses or sequential responses based on the route (<strong>NB: This will disable the rules tho</strong>)</p>

<p><img src="https://blog.shanelee.name/content/images/2021/08/Screen-Shot-2021-08-27-at-12-36-20-pm.png" alt="Mocking a REST API the "API first" approach with Mockoon"></p>

<p>It is even smart enough to convert certain fields <code>weatherType</code> and <code>status</code> to its templating language. How cool is that! And again, based on previous steps you can start the server and test it out via Insomnia.</p>

<p><img src="https://blog.shanelee.name/content/images/2021/08/Screen-Shot-2021-08-29-at-4-34-35-pm.png" alt="Mocking a REST API the "API first" approach with Mockoon"></p>

<p>Insomnia is such a powerful app also as it supports the "API first" approach. I can import in the exact same OAS file and test the endpoints. (You will start to see a recurring trend here of "API first" tools 😅) </p>

<h3 id="step2runmockanywhere">Step 2 Run mock anywhere</h3>

<p>Ok, so now we are happy with the mock responses, how can I run this anywhere? </p>

<h3 id="introducingmockooncli">Introducing Mockoon CLI 🎉🎉🎊🥳</h3>

<blockquote>
  <p>Mockoon's perfect complement for all your headless and automated environments.</p>
</blockquote>

<p>Mockoon CLI Supports all Mockoon's features, Lightweight and fast, and allows you to Run your mocks everywhere.</p>

<p>NB: Also available as a Docker image, run your mock APIs in Github Actions or on your favorite CI platform!</p>

<p>The CLI is a companion application to Mockoon's main interface designed to receive an exported Mockoon data file.</p>

<p>It has been written in JavaScript/TypeScript and uses some great libraries like <code>oclif</code> and <code>PM2</code>. One of the benefits of using PM2 is that you can easily manage your running mock APIs through the CLI or by using PM2 commands if you are used to them.</p>

<h3 id="step3installmockooncli">Step3. Install mockoon CLI</h3>

<p>I installed using nodeJS</p>

<pre><code class="language-bash">npm install -g @mockoon/cli
</code></pre>

<h3 id="step4exportyourmockapitoajsonfile">Step 4. Export your mock API to a JSON file</h3>

<p>To export your environment, open the <code>"Import/export"</code> application menu and choose <code>"Mockoon's format" -&gt; "Export all environments to a file (JSON)"</code> or <code>"Export current environment to a file (JSON)"</code>.</p>

<p>You can then select a location to save the export data file. Let's name the file <code>Greetings_Mockoon.json</code>.</p>

<h3 id="step5startyourmockapi">Step 5. Start your mock API</h3>

<p>After exporting your data file, you are ready to run your API mock with the CLI.</p>

<p>In your terminal, navigate to the folder where your export data file is and run the following command:</p>

<p><code>mockoon-cli start --data ./Greetings_Mockoon.json</code></p>

<p>If you want to use a remotely hosted file, you can also provide a URL to the --data flag like this:</p>

<p><code>mockoon-cli start --data https://domain.com/data-export.json</code></p>

<h3 id="step6manageyourapimock">Step 6. Manage your API mock</h3>

<p>After running one or more API server mock, you might want to check their health and statuses. To do so you can type <code>mockoon-cli list</code>:</p>

<pre><code class="language-bash">shanelee at shanes-MacBook-Air in ~/projects/mockoon-demo on main [?]  
$ mockoon-cli list
 Name                 Id   Status    Cpu    Memory    Hostname       Port
 ──────────────────── ──── ───────── ────── ───────── ────────────── ──────
 mockoon-greeting-api 0    online    0.5    71 MB     0.0.0.0        3002
</code></pre>

<p>You can also stop all running servers at once with <code>mockoon-cli stop all</code></p>

<h3 id="step7viewarunningmockslogs">Step 7. View a running mock's logs</h3>

<p>Mockoon CLI log all events like requests and errors in your user folder in the following files: <code>~/mockoon-cli/logs/{process_name}-out.log</code> and <code>~/mockoon-cli/logs/{process_name}-error.log</code>.</p>

<h3 id="step8deploymockooncliusingdocker">Step 8. Deploy Mockoon CLI using Docker</h3>

<p>Now to the fun part! The CLI can containerize your mock API for you. The docker base image is <code>node:14-alpine</code> which is very lightweight.</p>

<h3 id="usingthedockerizecommand">Using the dockerize command</h3>

<pre><code class="language-bash">mockoon-cli dockerize --data ./Greetings_Mockoon.json --port 3000 --index 0 --output ./Dockerfile
</code></pre>

<p>Now build the image:  </p>

<pre><code class="language-bash">    docker build -t mockoon-greeting-api .
</code></pre>

<p>And then run the container:</p>

<pre><code class="language-bash">    docker run -d -p 3000:3000 mockoon-greeting-api
</code></pre>

<p><a href="https://asciinema.org/a/432855"><img src="https://asciinema.org/a/432855.svg" alt="Mocking a REST API the "API first" approach with Mockoon" title=""></a></p>

<h3 id="step9usemockooncliinacienvironmentgithubactions">Step 9. Use Mockoon CLI in a CI environment: GitHub Actions</h3>

<p>Mockoon CLI being a Javascript application, it can run on any environment where Node.js is installed, including continuous integration systems like GitHub Actions, Buildkite, or CircleCI. It is useful when you want to run a mock server while running integration tests on another application. For example, you could mock the backend when running React front-end application tests. Or when you are running IT tests for a java based application that <code>integrates</code> with Greetings API.</p>

<p>Here is an example of a GitHub Action running a mock API (via docker) before running some tests:</p>

<pre><code class="language-yaml">name: Run mock API server

on: [push]

jobs:  
  run_integ_tests:
    name: Run integ tests
    runs-on: ubuntu-latest
    services:
      greetings:
        image: shanelee007/mockoon-greeting-api:latest
        ports:
          - 3000:3000

    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 11
      uses: actions/setup-java@v2
      with:
        java-version: '11'
        distribution: 'adopt'
    - name: Cache Maven packages
      uses: actions/cache@v2
      with:
        path: ~/.m2
        key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-m2
    - name: Build with Maven
      run: mvn --batch-mode -ff -V --update-snapshots verify
</code></pre>

<p>So just to summarise with the "API first" approach I can:</p>

<ul>
<li>import an OAS to mockoon</li>
<li>configure the routes (if needed)</li>
<li>set some rules</li>
<li>add dynamic templating to responses (more on this later)</li>
<li>export this file, containerise using the CLI and run it as part of my CI build. </li>
</ul>

<p>All within a matter of seconds!</p>

<h2 id="generatingdynamicdata">Generating dynamic data</h2>

<p>Mocking an API can save you time. By faking the backend responses early, you don't have to worry about whether an endpoint is ready or not. You are up and running in no time and can start implementing your application. However, your mock should still be realistic. And the examples provided in the OAS are often not enough to surface UI layout problems, container overflowed by text, etc.</p>

<p>When mocking using <code>Mockoon</code>, you can easily customize your endpoints to make them look like real ones and even behave realistically, thanks to the dynamic templating system.</p>

<h3 id="generaterandomfakedata">Generate random fake data</h3>

<p>Nowadays, most developers work with JSON. Generating a massive amount of fake JSON data with Mockoon is a breeze thanks to the powerful templating system based on Handlebars syntax.</p>

<p>Mockoon also offers multiple helpers and embarks the <code>Faker.js</code> library, which can generate localized random data as various as cities, addresses, first names, phone numbers, UUID, etc.</p>

<h4 id="completejsonexamplepostslist">Complete JSON example: posts list</h4>

<p>So let's revisit our Greetings API again. The API provides a <code>GET</code> request to return all greetings. So let's override the examples provided from OAS with much richer content.</p>

<p>By using a combination of <code>repeat</code>, <code>image.avatar</code>, <code>lorem.sentences</code>, etc. you can quickly get a massive amount of random data. Combined with the latency option, you can even simulate a slow server and check how your application behaves under stress.</p>

<p>To use the templating system, you only have to use the response body editor and start adding your content. Remember to use the double curly braces to delimit your helpers <code>{{ helperName }}</code> Let's have a look at what such a body could look like:</p>

<pre><code class="language-handlebars">[
  {{#repeat (queryParam 'total' '5')}}
  {
    "id": {{@index}},
    "message": "{{faker 'lorem.sentence' 3 5}}",
    "creationDate": "{{date '2020-11-20' '2020-11-25' "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"}}",
    "label": "key",
    "isFriendly": {{faker 'random.boolean'}},
    "weatherType": "{{oneOf (array '1' '2' '3')}}",
    "status": "{{oneOf (array 'SMILEY_FACE' 'SAD_FACE')}}"
  }
  {{/repeat}}
]
</code></pre>

<p>After a call to Mockoon, this would be the kind of body generated from this template:</p>

<pre><code class="language-json">[
  {
    "id": 0,
    "message": "Earum veritatis est.",
    "creationDate": "2020-11-24T17:32:33.293Z",
    "label": "key",
    "isFriendly": false,
    "weatherType": "1",
    "status": "SMILEY_FACE"
  },
  {
    "id": 1,
    "message": "Rerum ipsa autem.",
    "creationDate": "2020-11-23T10:30:00.526Z",
    "label": "key",
    "isFriendly": true,
    "weatherType": "3",
    "status": "SAD_FACE"
  },
  {
    "id": 2,
    "message": "Porro aut dolores.",
    "creationDate": "2020-11-22T21:03:58.452Z",
    "label": "key",
    "isFriendly": true,
    "weatherType": "1",
    "status": "SMILEY_FACE"
  },
  {
    "id": 3,
    "message": "Qui repudiandae quibusdam.",
    "creationDate": "2020-11-22T09:13:08.923Z",
    "label": "key",
    "isFriendly": true,
    "weatherType": "2",
    "status": "SAD_FACE"
  },
  {
    "id": 4,
    "message": "Qui et voluptatem.",
    "creationDate": "2020-11-22T08:36:19.770Z",
    "label": "key",
    "isFriendly": true,
    "weatherType": "1",
    "status": "SMILEY_FACE"
  }
]
</code></pre>

<p>This example makes extensive usage of what Mockoon and Faker.js have to offer. First, it generates as many "greetings" items as provided in the <code>total</code> query parameter (or default to 5) when calling GET <code>/your/endpoint?total=140</code>. It is especially useful when you want to request a specific number of items depending on the pagination or a "number per pages" user setting. Second, you can see that multiple properties are defined, and random mock data is generated like sentence, date-time, boolean etc.</p>

<p>There are a lot of possibilities and combinations you can try. You can also make your template react to a lot of parameters from the entering request by using Mockoon's helpers. We've already seen <code>queryParam</code> above, but you will find many more in the templating documentation. They allow you to query the request information like <code>body</code>, <code>urlParam</code>, <code>header</code>, <code>method</code>, etc.</p>

<blockquote>
  <p>Mockoon does not limit you to JSON. The templating language based on Handlebars is compatible with any content type. It means that you can generate CSV, HTML, XML, etc. You will find below some examples of what can you can achieve with the templating system.</p>
</blockquote>

<h4 id="generatedynamictemplatingdependingontherequest">Generate dynamic templating depending on the request</h4>

<p>We just saw some interesting use-cases but still quite simple. When working on your application, you may want to go a little bit further by making the template react to the request sent to Mockoon. This is possible by using various helpers that you will find in the templating documentation: <code>body</code>, <code>queryParam</code>, <code>urlParam</code>, <code>cookie</code>, <code>header</code>, <code>hostname</code>, <code>ip</code>, <code>method</code>, etc.</p>

<p>They allow you to access the entering request's information. Combined with other helpers like <code>repeat</code>, <code>switch</code>, or <code>if</code>, you will be able to dynamically generate more complex content.</p>

<p>You will find below some examples:</p>

<h5 id="newgreetingafterapostrequest">New greeting after a POST request</h5>

<p>We will reuse in the response the various parameters present in the request:</p>

<pre><code class="language-json">{
  "id": "{{faker 'random.uuid'}}",
  "message": "{{body 'message'}}",
  "creationDate": "{{date '2020-11-20' '2020-11-25' "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"}}",
  "label": "{{body 'label'}}",
  "isFriendly": {{body 'isFriendly'}},
  "weatherType": {{body 'weatherType'}},
  "status": "{{body 'status'}}"
}
</code></pre>

<p>After a call to this endpoint with the following body:</p>

<pre><code class="language-bash">POST /greetings  
Content-Type: application/json

{
  "message": "Hello Docker",
  "label": "key",
  "isFriendly": true,
  "weatherType": 0,
  "status": "SMILEY_FACE"
}
</code></pre>

<p>We would receive this kind of response content, containing the request information plus some new fields (id and creationDate):</p>

<pre><code class="language-json">{
  "id": "6df3c0c6-bce8-4094-ae29-5cb637fc15a3",
  "message": "Hello Docker",
  "creationDate": "2020-11-24T14:49:47.139Z",
  "label": "key",
  "isFriendly": true,
  "weatherType": 0,
  "status": "SMILEY_FACE"
}
</code></pre>

<p>For more complex cases or to test various error handling scenarios, you could also create multiple responses for the same route, with different bodies, and trigger them by defining some <code>rules</code>. To learn more about using multiple responses combined with rules, you can have a look at the related <a href="https://mockoon.com/docs/latest/route-responses/dynamic-rules/">documentation</a>.</p>

<p>Src files used in this post can be found on <a href="https://github.com/shavo007/mockoon-demo">github</a></p>]]></content:encoded></item><item><title><![CDATA[Jibbing with spring boot and google cloud run]]></title><description><![CDATA[<p>I said jibbing not jiving! 😆</p>

<h1 id="cloudnativearchitecture">Cloud native architecture</h1>

<p>When it comes to microservices and cloud native architecture you first think about containers. Now you can of course compose your own docker file. But with jvm based microservices there is a tool from google called jib that can simplify containerization.</p>

<p>We'll</p>]]></description><link>https://blog.shanelee.name/2020/08/29/jibbing-with-spring-boot-and-google-cloud-run/</link><guid isPermaLink="false">9dc3d48c-84cb-487a-b8ca-7d14229f1fe5</guid><category><![CDATA[gcp]]></category><category><![CDATA[cloudrun]]></category><category><![CDATA[spring-boot]]></category><category><![CDATA[spring]]></category><category><![CDATA[docker]]></category><category><![CDATA[microservice]]></category><category><![CDATA[cloud]]></category><category><![CDATA[google]]></category><category><![CDATA[jib]]></category><category><![CDATA[redoc]]></category><category><![CDATA[swagger]]></category><category><![CDATA[openapi]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Sat, 29 Aug 2020 08:08:34 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2020/08/ardian-lumi-6Woj_wozqmA-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.shanelee.name/content/images/2020/08/ardian-lumi-6Woj_wozqmA-unsplash.jpg" alt="Jibbing with spring boot and google cloud run"><p>I said jibbing not jiving! 😆</p>

<h1 id="cloudnativearchitecture">Cloud native architecture</h1>

<p>When it comes to microservices and cloud native architecture you first think about containers. Now you can of course compose your own docker file. But with jvm based microservices there is a tool from google called jib that can simplify containerization.</p>

<p>We'll take a simple Spring Boot application and build its Docker image using <a href="https://github.com/GoogleContainerTools/jib">Jib</a>. And then we'll also publish to GCR and deploy on google cloud run.</p>

<h2 id="jib">Jib</h2>

<blockquote>
  <p>Jib builds optimized Docker and OCI images for your Java applications without a Docker daemon - and without deep mastery of Docker best-practices. It is available as plugins for Maven and Gradle and as a Java library.</p>
</blockquote>

<p>You don't even need docker installed. Just use maven or gradle plugin and away you go! It uses <a href="https://github.com/GoogleCloudPlatform/distroless">distroless</a> base image under the hood, but as you expect all of this is configurable.</p>

<p>Jib supports multiple container registries,  can change the base image, jvm flags, tags, volumes and much more.</p>

<h2 id="greetingapp">Greeting App</h2>

<p>I use <strong>VSCode</strong> heavily for development so I wanted to see what support they have for java and spring boot. In fact they have many extensions. The extensions I installed are:</p>

<ul>
<li><a href="https://marketplace.visualstudio.com/items?itemName=richardwillis.vscode-gradle-extension-pack">Gradle Extension Pack</a></li>
<li><a href="https://aka.ms/vscode-java-installer-mac">VS Code for java</a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=Pivotal.vscode-spring-boot">Spring boot tools</a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-spring-initializr">Spring Initializr Java Support</a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-spring-boot-dashboard">Spring Boot Dashboard</a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=GabrielBB.vscode-lombok">Lombok Annotations Support for VS Code</a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi">OpenAPI (Swagger) </a></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync">Settings Sync </a> very important to sync if I change laptop in the future...</li>
</ul>

<p>I used Spring Initializr to create a simple project in vs code. <br>
It'll expose a simple <strong>GET</strong> endpoint:</p>

<pre><code class="language-bash">http://localhost:8080/greeting  
</code></pre>

<h3 id="deployment">Deployment</h3>

<p>Now I wanted to deploy this on cloud run and not worry about defining a docker file. So I used the gradle jib plugin and configured the credentials helper to deploy to <a href="https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#configuration">GCR</a> (Google cloud registry)</p>

<h4 id="cloudrun">Cloud run</h4>

<p>You can think of cloud run as CAAS (container-as-a-service) or serverless containers. It allows you to  run your stateless HTTP containers without worrying about provisioning machines, clusters or autoscaling. The main difference with app engine flexible is that it can scale to zero - only pay per request.</p>

<p>If you are interested to learn more about cloud run check out the unofficial <a href="https://github.com/ahmetb/cloud-run-faq">faqs</a></p>

<p>There is even a <a href="https://github.com/GoogleCloudPlatform/cloud-run-button">cloud run button</a> if you want to deploy your API publicly.</p>

<p>There is a fantastic VSCode extension called <a href="https://cloud.google.com/code">cloud code</a> that can help you deploy to cloud run or GKE on google cloud. It can even pick up if you are using <a href="https://blog.shanelee.name/2019/02/20/skaffold-for-local-kubernetes-development/">skaffold</a> or jib under the hood.</p>

<p><img src="https://blog.shanelee.name/content/images/2020/08/cloudcode1.png" alt="Jibbing with spring boot and google cloud run">
<img src="https://blog.shanelee.name/content/images/2020/08/cloudcode2.png" alt="Jibbing with spring boot and google cloud run"></p>

<p>This command will build, push your image to GCR and deploy to cloud run. And that's it!</p>

<p>Now you can access the public url and test out the endpoint.</p>

<p><img src="https://blog.shanelee.name/content/images/2020/08/Cloudcodeexp.png" alt="Jibbing with spring boot and google cloud run"></p>

<p>The source code for the example is over on <a href="https://github.com/shavo007/spring-boot-jib">github</a></p>

<h2 id="apidesign">API design</h2>

<p>In my previous <a href="https://blog.shanelee.name/2019/05/17/graphql-api-google-cloud-run/">post</a> I discussed graphQL. Graphql provides a schema for introspection and type safety. </p>

<p>Now for REST, swagger or <a href="https://swagger.io/resources/open-api/">open API spec</a> is the standard for designing and documenting your API. Even <a href="https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-specification">kubernetes API </a> supports open api spec. Every time you call <code>kubectl describe &lt;resource&gt;</code> it calls this endpoint for info.</p>

<p>Tools such as <a href="https://support.insomnia.rest/article/94-introduction">insomnia designer</a> and api gateways now support open api spec.</p>

<p>I decided to design and document my greetings API using <a href="https://github.com/Redocly/redoc">redoc</a></p>

<h3 id="redoc">Redoc</h3>

<blockquote>
  <p>OpenAPI/Swagger-generated API Reference Documentation</p>
</blockquote>

<p>Redoc supports open api spec v3 and provides responsive documentation with code samples. There is many ways to deploy but again Ill go the docker way and deploy on cloud run.</p>

<h4 id="redocgenerator">Redoc generator</h4>

<p>Redoc has a <a href="https://github.com/Redocly/create-openapi-repo">generator</a> which provides a docs-like-code approach to OpenAPI definitions. It allows you to validate your spec before bundling it. I documented my greetings API above, bundled it and served it as static content from the API <a href="https://github.com/shavo007/spring-boot-jib/blob/master/src/main/resources/static/swagger.yaml">itself</a></p>

<p><mark>One thing to note is you need to enable CORS in your API to serve the static file. I also had an issue with @EnableMVC annotation so I removed that.</mark></p>

<p>To deploy, I built redoc on GCR and deployed to cloud run. You can run this in cloud shell if you like</p>

<pre><code class="language-bash">export PROJECT_ID=$(gcloud config list --format 'value(core.project)')  
#change CLOUD_RUN_SPRING_BOOT_BASE_URI to the location of your API deployed on cloud run

gcloud run deploy redoc-greetings-api --project $PROJECT_ID --image gcr.io/$PROJECT_ID/redoc --platform managed --region us-central1 --port 80 --cpu 1 --memory 256Mi --concurrency 80 --timeout 300 --update-env-vars SPEC_URL=https://&lt;CLOUD_RUN_SPRING_BOOT_BASE_URI&gt;/swagger.yaml  
</code></pre>

<p><img src="https://blog.shanelee.name/content/images/2020/08/Redoc.png" alt="Jibbing with spring boot and google cloud run"></p>

<p>Again, the src code can be found on <a href="https://github.com/shavo007/greetings-doc">github</a></p>

<p>And thats it. Happy jibbing! 🕺🏻</p>]]></content:encoded></item><item><title><![CDATA[How I passed the professional google cloud architect exam]]></title><description><![CDATA[<h1 id="sowhygooglecloud">So why Google cloud??</h1>

<p>Well, the Google Cloud Platform (GCP) is behind the 8 ball for sure in comparison to AWS. But with the huge spike in companies looking to reduce their Capex and move to the cloud, its offerings in the data analytics space for example is very enticing.</p>]]></description><link>https://blog.shanelee.name/2020/08/04/how-i-passed-the-professional-google-cloud-architect-exam/</link><guid isPermaLink="false">f54e6eec-b076-47af-959c-23f838c318f6</guid><category><![CDATA[cloud]]></category><category><![CDATA[google]]></category><category><![CDATA[kubernetes]]></category><category><![CDATA[exam]]></category><category><![CDATA[gcp]]></category><category><![CDATA[google cloud]]></category><category><![CDATA[architect]]></category><category><![CDATA[bigquery]]></category><category><![CDATA[linux academy]]></category><category><![CDATA[cloud native]]></category><category><![CDATA[hybrid]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Tue, 04 Aug 2020 06:47:49 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2020/08/morning-brew-T0qYg2nPUWM-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<h1 id="sowhygooglecloud">So why Google cloud??</h1>

<img src="https://blog.shanelee.name/content/images/2020/08/morning-brew-T0qYg2nPUWM-unsplash.jpg" alt="How I passed the professional google cloud architect exam"><p>Well, the Google Cloud Platform (GCP) is behind the 8 ball for sure in comparison to AWS. But with the huge spike in companies looking to reduce their Capex and move to the cloud, its offerings in the data analytics space for example is very enticing. Also in my view, they have the best managed kubernetes service GKE. Which makes sense, when you understand that kubernetes originated from googles own internal cluster orcherstration service called <a href="https://research.google/pubs/pub43438/">borg</a>. </p>

<p>I have worked with AWS for many years professionally. Gaining exposure to other cloud providers is important when you see companies adopting a  multi-cloud approach but fundamentally many of the underlying patterns and managed services commonly exist across the top 3 (AWS, GCP, Azure).</p>

<p>Up until recently I did not fully understand where <a href="https://www.forbes.com/sites/janakirammsv/2020/07/19/why-bigquery-omni-is-a-big-deal-for-google-cloud-customers-and-partners/#3644f89b1843">Anthos</a> fit into the picture. But now I do. By bringing compute closer to the data, existing cloud users can now use the likes of GKE and BigQuery. Nicely played Google! <strong>If the mountain won't come to muhammed...</strong> ☺️ </p>

<h2 id="certification">Certification</h2>

<p>This <a href="https://cloud.google.com/certification/cloud-architect">certification</a> allowed me to analyse and understand the majority of GCP services. But also provide a solid understanding of architectural best practices when it comes to design considerations, security, reliability and cost optimisation.</p>

<h2 id="exampreparation">Exam Preparation</h2>

<p>There are several training providers out there that provide courses specific to this exam: Coursera, Udemy, Linux academy to name but a few.</p>

<h3 id="courseras">Coursera’s:</h3>

<ul>
<li><p><a href="https://www.coursera.org/specializations/gcp-architecture">Architecting with Google Compute Engine
</a></p></li>
<li><p><a href="https://www.coursera.org/learn/preparing-cloud-professional-cloud-architect-exam/">Preparing for the Professional Cloud Architect Examination</a></p></li>
</ul>

<h3 id="linuxacademys">Linux Academy’s:</h3>

<ul>
<li><a href="https://linuxacademy.com/cp/modules/view/id/321">Google Cloud Certified Professional Cloud Architect
</a></li>
</ul>

<p>I took the updated course from Linux Academy’s Google Cloud Certified Professional Cloud Architect.</p>

<p>Linux Academy’s updated course was a good choice as it covers the majority of exam related GCP based topics and its practice sessions are an added advantage associated after each lesson. Their <a href="https://interactive.linuxacademy.com/diagrams/MasterBuildersGuide.html">master builders guide</a> was a very important document that I referred back to many times.</p>

<p>I also like to be hands on, so I signed up to <a href="https://www.qwiklabs.com/">qwiklabs</a> and completed many of the quests they provide in relation to GCP. It's extremely helpful especially if you do not have your own google cloud account. </p>

<p>Google also provides a <a href="https://lp.cloudplatformonline.com/rs/808-GJW-314/images/Professional%20Cloud%20Architect%20Journey.pdf">learning path</a> as an alternative option.</p>

<h2 id="examregistration">Exam registration</h2>

<p><a href="https://webassessor.com/wa.do?page=publicHome&amp;branding=GOOGLECLOUD">Register</a> for the exam when you are about 1 week into your study. This allows you to book in a slot not too far in the future and keeps you motivated.</p>

<h2 id="examlayout">Exam layout</h2>

<blockquote>
  <p>The exam is two hours long. You can take it remotely or at a test centre ( I took it at home). There are 50 questions - multiple choice and multiple select. The GCP case studies take up about 12 questions with the rest, GCP in general.</p>
</blockquote>

<h3 id="examscore">Exam score</h3>

<p>The passing marks are not shared by Google in any of GCP exam except it tells about pass or fail. However it has been assumed from various blogs and training providers that it’s roughly around 80% for GCP exams though no official confirmation about it.</p>

<h3 id="examreadiness">Exam readiness</h3>

<p>Check your readiness by taking the <a href="https://forms.gle/SHcLhSXckievBNBn6">google practice exam</a> and Linux academys (if you have signed up for the course). If you are not scoring 90% or above, then keep studying!</p>

<h3 id="tips">Tips</h3>

<ul>
<li><p>You will have plenty of time to go back and review marked questions. So make sure if you are stuck, mark question and move on. </p></li>
<li><p>Eliminate answers that you know are incorrect</p></li>
<li><p>Look out for main keywords in the question - speeds, low latency, failover, serverless etc etc </p></li>
</ul>

<h2 id="finalsuggestions">Final suggestions</h2>

<ul>
<li><p>Remember to deeply learn Kubernetes, Bigdata services(Big Query, BigTable), NoSQL options, App engine, Storage, IAM, BigQuery Roles, Case studies..</p></li>
<li><p>Googles new <a href="https://cloud.google.com/architecture/framework">architecture framework</a> is a very useful resource too.</p></li>
</ul>

<p><img src="https://blog.shanelee.name/content/images/2020/08/googlecert.jpg" alt="How I passed the professional google cloud architect exam"></p>

<p>Still waiting on my bag!! Best of luck.</p>]]></content:encoded></item><item><title><![CDATA[Road to clean energy and sustainability]]></title><description><![CDATA[<blockquote>
  <p>This is an overview of my journey to reducing my carbon footprint and making my home as energy efficient as possible.</p>
</blockquote>

<h2 id="energyefficiency">Energy efficiency</h2>

<p>WFH myself since March, I did not realise how cold and under insulated australian households can be! I'm european and I feel colder here than winter time</p>]]></description><link>https://blog.shanelee.name/2019/11/28/road-to-clean-energy-and-sustainability/</link><guid isPermaLink="false">e710a094-36a0-417a-980d-ad93850aa892</guid><category><![CDATA[solar]]></category><category><![CDATA[clean energy]]></category><category><![CDATA[melbourne]]></category><category><![CDATA[sustainability]]></category><category><![CDATA[cycle]]></category><category><![CDATA[bicycle]]></category><category><![CDATA[climatechange]]></category><category><![CDATA[renewables]]></category><dc:creator><![CDATA[Shane Lee]]></dc:creator><pubDate>Thu, 28 Nov 2019 03:20:01 GMT</pubDate><media:content url="https://blog.shanelee.name/content/images/2019/11/matt-duncan-IUY_3DvM__w-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<blockquote>
  <img src="https://blog.shanelee.name/content/images/2019/11/matt-duncan-IUY_3DvM__w-unsplash.jpg" alt="Road to clean energy and sustainability"><p>This is an overview of my journey to reducing my carbon footprint and making my home as energy efficient as possible.</p>
</blockquote>

<h2 id="energyefficiency">Energy efficiency</h2>

<p>WFH myself since March, I did not realise how cold and under insulated australian households can be! I'm european and I feel colder here than winter time in Vienna at minus 10 degrees! 🥶</p>

<blockquote>
  <p>We’re lagging between 10-15 years behind in Europe, parts of US and Canada,” says Trivess Moore, a senior lecturer in construction at RMIT and member of the Sustainable Building Innovation Laboratory.</p>
</blockquote>

<p>You can read more on this topic where a housing estate project looked at building homes with at least an energy rating of <strong>7.5</strong> and the outcomes that came from this in relation to cost savings  but also wellbeing <br>
<a href="https://onestepoffthegrid.com.au/even-with-rooftop-solar-boom-consumers-are-paying-dearly-for-what-lies-underneath/">https://onestepoffthegrid.com.au/even-with-rooftop-solar-boom-consumers-are-paying-dearly-for-what-lies-underneath/</a></p>

<p>So what can we do about it?? Well, one recommendation I would like to see is for state and local councils to work with property developers and provide incentives to build energy efficient homes. But in the meantime, what can a homeowner do to rectify the situation... 🤔</p>

<h2 id="renewables">Renewables</h2>

<p>Last year, I installed solar panels on my house. This is something I had always planned to do when I purchased my own home. With Victorias rebate program, there is a huge uptake in residents installing solar. And why wouldn't you! Reduce your energy costs over time and become less reliant on the grid and our coal-burning plants. 🌍</p>

<h3 id="solar">Solar</h3>

<p><img src="https://blog.shanelee.name/content/images/2019/11/antonio-garcia-ndz_u1_tFZo-unsplash.jpg" alt="Road to clean energy and sustainability"></p>

<p>Before I chose a solar installer to go with, I researched online to understand the best solution for my home.</p>

<p>Clean energy council (CEC) has a great guide on solar <a href="https://www.cleanenergycouncil.org.au/consumers">here</a>. I choose not to get a battery now as they are still quite expensive. But with the rapid innovation in this space, it won't be long in my view before it becomes attainable. </p>

<p><em>Victoria energy council has expanded the <a href="https://www.solar.vic.gov.au/solar-battery-rebate">battery rebate</a> to certain postcodes if applicable.</em></p>

<p>Finn at <a href="https://www.solarquotes.com.au/solar101.html">solar quotes</a> has invaluable information on choosing the right panels, inverters and provides quotes for local reviewed installers.</p>

<p>I contacted around five or six installers (all CEC accredited) and was surprised by the results. One actually refused to do a house inspection and focused solely on nearmap satellite imagery. Another stated that it was not worth installing based on their panel placement! </p>

<h4 id="systemchoice">System choice</h4>

<p>So the system I decided on was <strong>6.27KW</strong> Solar System with:</p>

<ul>
<li>19x Q-Cells Dual Cell Q.Peak Duo G5-330W</li>
<li>19x Micro-Inverters Enphase IQ7+</li>
<li>1x Envoy S-Metered + DRM</li>
<li>1x Sub-board installation to fit the Envoy relays and allow room for future use</li>
</ul>

<p>All panels were installed west facing.</p>

<p><img src="https://blog.shanelee.name/content/images/2019/11/enphase.gif" alt="Road to clean energy and sustainability"></p>

<p><strong>Enphase Micro-Inverters</strong> make each panel run independently, which is a big advantage compared to a string inverter system where when one panel starts to act faulty, it shuts down the entire array of panels.</p>

<p>It also comes with a monitoring system, the Enphase enlighten view, which allows you to monitor not only the PV production of your solar system but also your home electricity consumption.</p>

<p>More information on micro inverters can be found <a href="https://www.cleanenergyreviews.info/blog/microinverters">here</a></p>

<p>There are some myths about installing on the south for example but it really depends on where you live as Finn <a href="https://www.solarquotes.com.au/panels/direction/">explained</a>, but it will inevitably still produce energy! It all comes back to <strong>self consumption</strong></p>

<p>I have the monitoring setup using enlighten and have a cheeky look every day to see my usage and production.</p>

<p>Below is the energy produced for a typical summers day in Melbourne.</p>

<p><img src="https://blog.shanelee.name/content/images/2020/08/Screenshot_20200819-112109-1.png" alt="Road to clean energy and sustainability"></p>

<p>There is a great sense of satisfaction knowing I am using clean energy to power my house during the day.</p>

<h4 id="selfconsumption">Self consumption</h4>

<p>I mentioned earlier about self-consumption. As my wife and I both work during the day (pre-covid), we try to utilize solar power when we can.</p>

<p>For example, I turn on the dishwasher before I leave for work in the morning and set the washing machine on a timer to turn on at peak time around 12-1 pm during the day.</p>

<p>Here is a great <a href="https://www.solar.vic.gov.au/making-most-solar">case study</a> of how a young family self consume.</p>

<p>It is important to understand that the feed-in tariff (FIT) most likely will fluctuate in the future (in VIC Jul 2020 reduced from 12c to 10.2c) so try and use up as much clean energy as possible.</p>

<p>Lastly, here is a fantastic write up of a resident using <a href="https://onestepoffthegrid.com.au/analysis-of-my-home-battery-solar-systems-first-year-performance/">solar</a> for a year</p>

<h2 id="anenergyefficienthome">An energy efficient home</h2>

<p>After installing solar, I looked into other areas I could make my home more energy efficient.</p>

<p>I stumbled across <a href="https://www.victorianenergysaver.vic.gov.au/save-energy-and-money/discount-energy-saving-products/save-with-these-energy-efficient-products">vic energy saver site </a> that provides savings for energy efficient products.</p>

<p>I had existing halogen downlights that I wanted to replace with LEDs and this initiative allowed me to replace all for <strong>FREE</strong>! </p>

<p>I used an accredited <a href="https://www.easybeinggreen.com.au/">provider</a> and it was pretty painless. They replaced all 40 of my downlights (apart from my dimmers). <strong>#winning</strong></p>

<p>The program even supports showerheads, weather sealing, and hotwater systems. Buying energy efficient appliances such as your oven, heat pump dryer, washing machine will make a difference too.</p>

<h3 id="getaninspection">Get an inspection</h3>

<p>Finally, if you want you can look into an <a href="https://www.victorianenergysaver.vic.gov.au/save-energy-and-money/get-a-home-energy-assessment">energy assessment</a> of your house.</p>

<h2 id="waste">Waste</h2>

<p>I was happy to see the Vic government bring in the plastic bag ban. Definitely well overdue. I believe in Ireland it came into play about ten years ago! </p>

<p>Since China stopped accepting alot of our waste the government has been scrambling to fix the waste management issue. I am happy to see Vic government outlining their <a href="https://www.vic.gov.au/sites/default/files/2020-02/Recycling%20Victoria%20A%20new%20economy.pdf">plan.</a> Highlights are:</p>

<ul>
<li>Four bin system 🙌🏻</li>
<li>Container deposit scheme</li>
<li>Circular economy</li>
</ul>

<p>I actually saw new <a href="https://www.yarracity.vic.gov.au/services/roads-and-traffic/wellington-street-bike-lanes--stage-2">popup bicycle lanes</a> in fitzroy north recently using recycled plastic 👍</p>

<p>More info on using recycled materials can be found <a href="https://www.sustainability.vic.gov.au/en/About-us/Latest-news/2020/08/14/00/50/New-funding-to-use-recycled-materials">here</a></p>

<h3 id="foodwaste">Food waste</h3>

<blockquote>
  <p>Each year in Victoria households throw out 250,000 tonnes worth of food – enough wasted food to fill Melbourne's Eureka Tower. 😱</p>
</blockquote>

<p>My wife and I have made a joint effort when shopping to buy fruit and veg loosely if possible. And also buy organically too. We have trialed growing some herbs and vegetables at home. Trial and error! But a great learning experience.</p>

<p>I listened to a podcast recently with Dr. Sandro Demaio (CEO of VicHealth) on <a href="https://open.spotify.com/episode/6w4JIeLLrnqUIDsX4jMC3E?si=nhJmzw81RtWAyH57FoNDGA">food</a> and its impact on society, our ecosystem and health. Less red meat people!!  <strong>#votewithyournote</strong></p>

<p>We also bring our <a href="https://www.redcycle.net.au/">scrunchy plastic</a> to the supermarket. Both coles and woolworths support the initiative.</p>

<h2 id="transport">Transport</h2>

<p>After energy, transport is the biggest carbon emitter. </p>

<blockquote>
  <p>Transport in Australia contributes around 100 million tonnes of greenhouse gasses into our atmosphere every year.</p>
</blockquote>

<p>The main reasons for transport emissions trending upwards are an over-dependence on cars with high average fuel use and an over-reliance on energy-intensive road freight.</p>

<p>So it comes to my favourite topic of all active transport and cycling!</p>

<h3 id="activetransport">Active transport</h3>

<p>Anyone who knows me, knows how I love cycling. I learned to cycle from a very young age in the irish countryside before you had to worry about cars on the road. Wherever I have worked and lived, whether in europe or australia my preferred mode of transport is the bike. For me, I love the independence and time alone on the bike. Plus it's a great way to wake up in the morning (especially if you need to cycle down chapel st 🤣)</p>

<p>Anytime my wife and I go abroad, I always go check out a city's shared bicycle scheme if available and explore the city by bike. This to me is the best way to see a city. On a warm summer evening cycling down along the banks of the river seine in Paris listening to some jazz and watching the world go by is pure bliss. 🇫🇷</p>

<p><img src="https://blog.shanelee.name/content/images/2020/08/IMG-20180522-WA0000-1.jpg" alt="Road to clean energy and sustainability"></p>

<p>Since covid there has been a huge uptake in cycling. I notice it myself at the weekend and on the trails. Governments like <a href="https://ecf.com/news-and-events/news/ireland-will-invest-10-total-transport-capital-budget-cycling?s=09">ireland</a> and the <a href="https://www.forbes.com/sites/carltonreid/2020/07/27/well-build-thousands-of-miles-of-protected-cycleways-pledges-boris-johnson/">UK</a> have invested heavily in protected cycleways and pedestrian infrastructure. </p>

<p>Change is coming, there is no doubt about it. The new paris mayor Anne Hidalgo has called it the <a href="https://www.theguardian.com/world/2020/feb/07/paris-mayor-unveils-15-minute-city-plan-in-re-election-campaign">15 minute city</a> idea. That is to have all services within a 15 minute walk/ride from your home. The days of commuter towns and work in the city after covid will not be the same. Working from home and even coworking spaces in rural areas will become more mainstream I predict.</p>

<blockquote>
  <p>“We need to reinvent the idea of urban proximity,” Moreno says. “We know it is better for people to work near to where they live, and if they can go shopping nearby and have the leisure and services they need around them too, it allows them to have a more tranquil existence.</p>
</blockquote>

<h3 id="electricvehicles">Electric vehicles</h3>

<p>Just a quick mention on EVs. I am still waiting for the national EV strategy to be released in Australia. It has been delayed many times. Nudge, nudge Angus Taylor...</p>

<p>Now take <a href="https://thedriven.io/2020/07/10/the-electric-recipe-of-norways-zero-emissions-transport-boom/">Norway</a> for example - The Norwegian Parliament has decided on a goal that all new cars sold by <strong>2025</strong> should be zero (battery electric or hydrogen) emission vehicles. </p>

<p>Unfortunately for alot of aussies, EVs are just too expensive right now and there is no incentive to switch.</p>

<h2 id="investingforthefuture">Investing for the future</h2>

<p>I just finished Ross Garnuat book recently <a href="https://books.google.com.au/books/about/Superpower.html?id=KPiPDwAAQBAJ&amp;printsec=frontcover&amp;source=kp_read_button&amp;redir_esc=y#v=onepage&amp;q&amp;f=false">Superpower: Australia's low carbon opportunity</a> and our renewable investment needs to increase dramatically. The IMF says Australia should be spending more on infrastructure, but this should be on rail, airports and seaports, <strong>rather</strong> than roads.</p>

<p>With low interest rates for the forseeable future, Deloitte, Grattan institute, and many others have stated now is the time to invest and invest smartly. The IMF has called this "great reset" an opportunity for fiscal stimulus that is <strong>smarter</strong>, <strong>greener</strong> and <strong>fairer</strong>. So lets push ahead and be bold for a change.</p>

<h2 id="finally">Finally</h2>

<p>I know reducing our carbon footprint sounds challenging and some feel overwhelmed by it all. But I hope this article helps educate others and know there is support out there. Albert Einstein once said: </p>

<blockquote>
  <p>“In the middle of difficulty lies opportunity.” </p>
</blockquote>

<p>We <strong>all</strong> have an opportunity to make a difference and create a more sustainable future. </p>]]></content:encoded></item></channel></rss>