<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://rswitzer.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://rswitzer.github.io/" rel="alternate" type="text/html" /><updated>2026-04-30T15:04:04+00:00</updated><id>https://rswitzer.github.io/feed.xml</id><title type="html">The Architect Who Codes | Hands-on Development for Better Decision-Making</title><subtitle>The best architects are also the best engineers. This blog is my commitment to bridging the gap. I am dedicating time to hands-on upskilling with the modern tech stack, ensuring that every architectural decision I make is informed by current, real-world development experience. It&apos;s about enhancing my craft and delivering more practical, high-quality architectures. Sharing my journey to inspire others.</subtitle><entry><title type="html">The New Disciplines: Engineering in a World That Got Flipped Overnight</title><link href="https://rswitzer.github.io/2026/04/30/the-new-disciplines.html" rel="alternate" type="text/html" title="The New Disciplines: Engineering in a World That Got Flipped Overnight" /><published>2026-04-30T00:00:00+00:00</published><updated>2026-04-30T00:00:00+00:00</updated><id>https://rswitzer.github.io/2026/04/30/the-new-disciplines</id><content type="html" xml:base="https://rswitzer.github.io/2026/04/30/the-new-disciplines.html"><![CDATA[<p>If you’ve been writing software professionally for ten or twenty years, the last eighteen months have been disorienting. Your instincts didn’t disappear. Pattern recognition built from a thousand bugs, a feel for when an abstraction is wrong, the muscle memory of typing out a function before your conscious mind has caught up. All of that is still there. What changed is the leverage. The job is the same job. The work is different work.</p>

<p>I spend a lot of time inside enterprises right now, and I see the same scene play out: a leadership mandate to “use AI more,” a procurement decision between Claude Code, GitHub Copilot, and Cursor, a flurry of activity, and six months later… not much has changed. Throughput hasn’t doubled. Quality hasn’t improved. A few developers swear by their tool of choice; others quietly turn it off. The mandate gets renewed and the cycle starts again.</p>

<p>The problem isn’t the tool. The problem is that nobody learned how the tool actually works.</p>

<p>This post is for engineers who feel that gap. You’re not skeptical of AI. You’ve used it. You’re skeptical of the hype, and you’re frustrated that your team’s investment isn’t compounding. Good. That skepticism is the right starting point. Let’s talk about what’s actually changed, what to learn, and where to put your energy on Monday morning.</p>

<h2 id="why-vibe-coding-is-failing-enterprises">Why “Vibe Coding” Is Failing Enterprises</h2>

<p>Vibe coding is the practice of describing what you want, accepting whatever the model produces, and shipping it if the tests pass. It works for prototypes. It catastrophically fails in enterprise codebases, and the reasons are structural, not cultural.</p>

<p>Three failures, every time:</p>

<ol>
  <li><strong>Inconsistency.</strong> Eight developers each prompt the agent slightly differently, get back eight subtly different solutions to the same shape of problem, and merge them all. Six months later, you have eight idioms for HTTP retry, four error-handling philosophies, and a codebase that pattern-matches against itself badly the next time an agent reads it.</li>
  <li><strong>No validation.</strong> The model said it works. The tests pass. Nobody actually read the diff. The bug ships, and the engineer who “wrote” it can’t debug it because they never engaged with the logic.</li>
  <li><strong>Drift.</strong> Without explicit guardrails, agents make plausible local choices that violate global constraints. They reach for a library you’ve banned. They write to a database directly when your architecture says go through the service layer. Each instance is small. Together they erode the codebase.</li>
</ol>

<p>None of these are model failures. They’re system-design failures. The agent did what it was asked. Nobody told it what <em>not</em> to do.</p>

<h2 id="how-agentic-ai-actually-works">How Agentic AI Actually Works</h2>

<p>To use these tools well, you have to understand two things: the agentic loop, and the context window. Most of the people I work with have a fuzzy intuition about both, but no operational model. Let’s fix that.</p>

<h3 id="the-agentic-loop">The Agentic Loop</h3>

<p>A modern coding agent isn’t a fancy autocomplete. It’s a loop:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  [User intent]
       |
       v
  [Model reasons] &lt;----------+
       |                     |
       v                     |
  [Need a tool?] -- yes --&gt; [Call tool] --&gt; [Result into context]
       |
       no
       v
  [Final response]
</code></pre></div></div>

<p>The model doesn’t “know” your codebase. At each step, it decides what it needs (reading a file, running a test, searching for a symbol) and the result of that action gets appended to its context. Then it reasons again. The loop continues until the model decides the task is complete or it hits a limit.</p>

<p>This has implications most teams haven’t internalized:</p>

<ul>
  <li><strong>Every tool call is a chance to give the agent better information, or worse information.</strong> A flaky test that returns gibberish poisons the loop. A linter that surfaces real issues sharpens it.</li>
  <li><strong>The agent’s “memory” is whatever is currently in the context window.</strong> It does not remember the last conversation. It does not remember yesterday. It remembers what’s loaded right now.</li>
  <li><strong>Time-to-feedback matters.</strong> A test suite that takes ten minutes to run is a ten-minute interruption in the agent’s reasoning. Fast, deterministic feedback loops compound.</li>
</ul>

<h3 id="the-context-window">The Context Window</h3>

<p>The context window is the working memory the model has access to during a single task. Everything the agent “knows” in this moment lives there: your prompt, the files it has read, tool outputs, conversation history, and any standing instructions.</p>

<p>The window has a fixed size. When it fills up, things get evicted or compacted, and quality degrades. This is why two superficially similar tasks can produce wildly different results: one of them was given a clean, focused window with the right files; the other was 80% full of irrelevant junk before the work even started.</p>

<p>Different models have different window sizes and different attention profiles. Larger windows aren’t automatically better. A model can get lost in a 200K-token window if half of it is noise. The skill isn’t filling the window. The skill is <strong>curating</strong> it.</p>

<h3 id="models-are-not-interchangeable">Models Are Not Interchangeable</h3>

<p>A fast, cheap model is great for narrow, well-scoped tasks: rename this variable, write this test, format this file. A larger reasoning model earns its cost on tasks that require holding multiple constraints simultaneously: refactor this module, design this API, debug this race condition.</p>

<p>Teams that use one model for everything are either overpaying or underperforming. Teams that match the model to the task, often inside the same workflow, get dramatically better economics.</p>

<h2 id="the-new-disciplines">The New Disciplines</h2>

<p>This is where vocabulary matters. The industry has settled, more or less, on three terms that describe three different jobs. They overlap, but they’re distinct, and engineers who confuse them under-invest in two of the three.</p>

<h3 id="prompt-engineering">Prompt Engineering</h3>

<p>The immediate instruction. “Refactor this function to use async/await.” “Write a test for the edge case where the user is null.”</p>

<p>Prompt engineering used to be most of the conversation. It’s now the smallest part of the job. Models are good enough that a clear, specific prompt usually suffices. If you find yourself spending a lot of time crafting the perfect prompt, your effort is probably misallocated. The leverage is upstream.</p>

<h3 id="context-engineering">Context Engineering</h3>

<p>Deciding what the model sees when it works. Which files are loaded. Which documentation is in scope. Which conventions are stated explicitly. Which past decisions are surfaced.</p>

<p>Context engineering is where most of the quality gap lives. Two teams using the same tool with the same prompts will produce wildly different output if one of them has invested in:</p>

<ul>
  <li>A <code class="language-plaintext highlighter-rouge">CLAUDE.md</code> (or equivalent for your tool) at the root of the repo that states architectural conventions, banned libraries, testing expectations, and domain terminology.</li>
  <li>Curated reference docs the agent can pull in on demand.</li>
  <li>Clean, idiomatic code that the agent can pattern-match against.</li>
  <li>Explicit examples of “we do it this way, not that way.”</li>
</ul>

<p>This is the work most enterprises skip, and it’s the work with the highest ROI.</p>

<h3 id="harness-engineering">Harness Engineering</h3>

<p>Designing the system the agent operates inside. The hooks, the validation gates, the subagents, the tool integrations, the CI checks, the rollback mechanisms.</p>

<p>If context engineering is “what does the agent know,” harness engineering is “what is the agent allowed to do, and what happens when it tries.” This is the discipline that turns a useful coding assistant into a reliable team member. It’s also the most underdeveloped at every enterprise I’ve worked with.</p>

<p>A team without a harness is trusting vibes. A team with a harness is running a system.</p>

<h2 id="the-primitives-hooks-skills-and-agents">The Primitives: Hooks, Skills, and Agents</h2>

<p>These are the building blocks you use to do the work above. Each maps onto one of the three disciplines.</p>

<h3 id="hooks-harness-engineering">Hooks (Harness Engineering)</h3>

<p>Hooks are deterministic checkpoints that fire at specific points in the agent’s loop: before a tool runs, after a file is edited, when a session starts. They are not polite suggestions to the model. They are code that runs, and the agent has to live with the result.</p>

<p>A trivial example: every time the agent edits a TypeScript file, run the type checker and the linter. Reject the edit if they fail.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"hooks"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"PostToolUse"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"matcher"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Edit|Write"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"hooks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"command"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npx tsc --noEmit &amp;&amp; npx eslint $CLAUDE_FILE_PATHS"</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This single hook eliminates an entire category of garbage commits. The agent cannot ship code that doesn’t compile, because the hook makes it impossible. You haven’t asked the model to be careful. You’ve made carelessness fail loudly.</p>

<p>Good hooks to consider:</p>

<ul>
  <li>Format-on-write</li>
  <li>Lint-on-write</li>
  <li>Block edits to sensitive paths (<code class="language-plaintext highlighter-rouge">/infra</code>, <code class="language-plaintext highlighter-rouge">/secrets</code>, generated files)</li>
  <li>Run affected tests after a change</li>
  <li>Log every shell command for audit</li>
</ul>

<p>Hooks are pure harness engineering: they are the rails the agent runs on.</p>

<h3 id="skills-context-engineering">Skills (Context Engineering)</h3>

<p>Skills are packaged expertise. A folder containing instructions, examples, and sometimes scripts, which the agent loads when relevant. Think of a skill as “the team’s knowledge about how to do X, written down once, available everywhere.”</p>

<p>A <code class="language-plaintext highlighter-rouge">SKILL.md</code> for your team’s database migration workflow might describe the conventions, the commands, the gotchas, and the review checklist. When a developer asks the agent to write a migration, the skill loads, and the agent inherits your team’s institutional knowledge instead of inventing its own.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">db-migrations</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">Use when creating, modifying, or reviewing database migrations.</span>
  <span class="s">Triggers on phrases like 'migration', 'schema change', 'alter table',</span>
  <span class="s">or file references under /migrations.</span>
<span class="nn">---</span>

<span class="gh"># Database Migrations</span>

<span class="gu">## Conventions</span>
<span class="p">-</span> All migrations are reversible. Every <span class="sb">`up`</span> has a tested <span class="sb">`down`</span>.
<span class="p">-</span> Never drop a column in a single migration. Two-phase: deprecate, then drop.
<span class="p">-</span> Index changes go in their own migration, never mixed with schema changes.

<span class="gu">## Workflow</span>
<span class="p">1.</span> Generate migration: <span class="sb">`npm run db:new &lt;name&gt;`</span>
<span class="p">2.</span> Write <span class="sb">`up`</span> and <span class="sb">`down`</span>. Both must be idempotent.
<span class="p">3.</span> Test the round-trip locally before pushing.
<span class="p">4.</span> Tag the PR with <span class="sb">`migration`</span> so the on-call DBA reviews it.
</code></pre></div></div>

<p>Skills are context engineering: they shape what the agent knows, on demand, scoped to the task.</p>

<h3 id="agents-and-subagents-both">Agents and Subagents (Both)</h3>

<p>A subagent is a specialized agent invoked from within another agent’s loop. It has its own context window, its own focused instructions, and it returns only its conclusion to the parent. The parent’s context stays clean.</p>

<p>Example: your main agent is implementing a feature and needs to understand how a legacy module works. Instead of dumping 4,000 lines of code into the main context (and degrading every subsequent decision), it dispatches a “code explorer” subagent that reads the legacy module and returns a 200-word summary. The main agent stays focused. The work gets done.</p>

<p>Subagents straddle both disciplines: defining what they do is context engineering; defining when and how they’re invoked is harness engineering.</p>

<h2 id="brownfield-cleanup-is-the-highest-leverage-ai-investment">Brownfield Cleanup Is the Highest-Leverage AI Investment</h2>

<p>Here’s the thing nobody wants to hear: the best thing you can do to make your AI tooling work better is <strong>clean up your codebase</strong>.</p>

<p>Agents pattern-match. They read your code, infer the conventions, and produce more of the same. If your codebase is consistent, well-organized, and idiomatic, the agent extends it correctly. If your codebase is a graveyard of half-finished refactors, three competing styles, and dead code, the agent confidently produces more of the mess.</p>

<p>This is the opposite of the order most teams attempt. They want to point the agent at the legacy mess and have it clean things up. That works at the margins. It does not work at scale, because the agent has no anchor for what “good” looks like in your context.</p>

<p>A more productive sequence:</p>

<ol>
  <li><strong>Pick one module or domain.</strong> Don’t boil the ocean.</li>
  <li><strong>Establish the patterns by hand or in tight collaboration with the agent.</strong> Two or three reference implementations done well.</li>
  <li><strong>Document them.</strong> In a <code class="language-plaintext highlighter-rouge">CLAUDE.md</code>, in skills, in architecture decision records.</li>
  <li><strong>Now point the agent at the rest.</strong> With pattern anchors and explicit guidance, it can extend the cleanup at scale.</li>
</ol>

<p>Your codebase is the single largest piece of context any agent will ever load. Investing in its quality is investing directly in the quality of every future AI-assisted change.</p>

<h2 id="we-are-still-professional-developers">We Are Still Professional Developers</h2>

<p>Here is where I want to push back on the discourse, gently. The narrative that “AI writes the code now” is half-true and badly framed. Yes, the typing has shifted to the model. But typing was never the job. The job was, and is:</p>

<ul>
  <li>Understanding the problem precisely enough to specify it</li>
  <li>Choosing constraints that make the system robust</li>
  <li>Validating that the work actually solves the problem</li>
  <li>Owning the result</li>
</ul>

<p>What changed is the medium. We used to express our judgment in code. We now express it in <strong>guardrails and descriptiveness</strong>: in the conventions we write down, the hooks we wire up, the skills we package, the contexts we curate, the validation we insist on. The agent does the keystrokes. We design the system that makes those keystrokes correct.</p>

<p>This is not less engineering. In some ways it is more. The discipline of being explicit is real work that most teams have avoided for decades. Writing down what was previously tribal knowledge. Codifying what was previously taste. That work is now the work.</p>

<p>The people I see succeeding with these tools are not the ones who type the cleverest prompts. They are the ones who treat the agent as a powerful, fast, senior teammate who needs context, needs guardrails, and needs to be checked. They test as they go. They validate every diff. They keep their codebase clean so the agent stays sharp. They are, in every sense that matters, still professional developers.</p>

<p>The floor moved. The craft didn’t.</p>

<h2 id="where-to-start">Where to Start</h2>

<p>If you take one thing from this post: pick a single repo this week and add three things.</p>

<ol>
  <li>A <code class="language-plaintext highlighter-rouge">CLAUDE.md</code> (or your tool’s equivalent) at the root that states your conventions, your stack, your banned patterns, and your testing expectations. Two pages, no more.</li>
  <li>One hook that enforces a thing you actually care about: lint, type check, test coverage, whatever. Make it impossible to ship code that violates it.</li>
  <li>A short audit of one area of the codebase where the patterns are inconsistent. Pick the canonical pattern, document it, and start the cleanup.</li>
</ol>

<p>That is a week of work. Slow down to speed up. It will compound for the rest of the year. And it is an order of magnitude more valuable than another debate about which tool is best.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[If you’ve been writing software professionally for ten or twenty years, the last eighteen months have been disorienting. Your instincts didn’t disappear. Pattern recognition built from a thousand bugs, a feel for when an abstraction is wrong, the muscle memory of typing out a function before your conscious mind has caught up. All of that is still there. What changed is the leverage. The job is the same job. The work is different work.]]></summary></entry><entry><title type="html">Back in Blog Mode (and Fresh off the GH-200)</title><link href="https://rswitzer.github.io/github/2026/01/31/github-actions-certification.html" rel="alternate" type="text/html" title="Back in Blog Mode (and Fresh off the GH-200)" /><published>2026-01-31T00:00:00+00:00</published><updated>2026-01-31T00:00:00+00:00</updated><id>https://rswitzer.github.io/github/2026/01/31/github-actions-certification</id><content type="html" xml:base="https://rswitzer.github.io/github/2026/01/31/github-actions-certification.html"><![CDATA[<p>I haven’t blogged in a bit, and my home lab has definitely taken a back seat lately. The reason? I’ve been heads-down on certifications.</p>

<p>Just a few weeks ago, I wrapped up the <strong>KCNA</strong>, and <strong>today I officially passed the GitHub Actions (GH-200) exam</strong>.</p>

<p>As an architect, I actually enjoy taking exams. Not because I love tests, but because they force accountability. When you work at the architecture level long enough, it’s easy to drift away from the implementation details. Certifications give me a concrete way to validate that I’m still absorbing the material and paying attention to the finer points. The stuff you don’t always hit when you’re doing POCs or designing high-level architectures.</p>

<p>Accountability looks different for everyone, but for me, certs are a reliable forcing function to stay sharp.</p>

<h2 id="why-im-sharing-this-while-its-still-fresh">Why I’m Sharing This (While It’s Still Fresh)</h2>

<p>Since I’m only a few hours removed from hitting that <strong>“pass”</strong> screen, I figured now was the perfect time to share a few study tips and the cheat sheet I built while prepping.</p>

<p>This is very much a <em>“here’s what worked for me”</em> post.</p>

<h2 id="how-i-studied">How I Studied</h2>

<p>I like to use <strong>multiple sources</strong> when learning a new technology. I’ve found that reviewing the same material from two to three different angles really helps reinforce concepts.</p>

<p>My usual pattern looks like this:</p>

<ul>
  <li><strong>1 primary reading resource</strong></li>
  <li><strong>1–2 video resources</strong></li>
</ul>

<p>For this exam, I reviewed the following:</p>

<h3 id="reading">Reading</h3>

<ul>
  <li><a href="https://learn.microsoft.com/en-us/training/courses/gh-200t00">Microsoft’s Offical GH-200 Course Material</a></li>
</ul>

<h3 id="video">Video</h3>

<ul>
  <li><a href="https://youtu.be/8m9JBtGFMp8?si=ocC69l7fL2AKorGK">Microsoft’s Offical GH-200 Video Training Course</a></li>
  <li><a href="https://youtu.be/Tz7FsunBbfQ?si=m2qPuOQLWenOZFY_">Andrew Brown’s GH-200 Course</a></li>
</ul>

<h2 id="practice-exams">Practice Exams</h2>

<p>For practice exams, I stuck with:</p>

<ul>
  <li><a href="https://learn.microsoft.com/en-us/credentials/certifications/github-actions/practice/assessment?assessment-type=practice&amp;assessmentId=1001&amp;practice-assessment-type=certification">Microsoft Learning Practice Questions</a> - I actually think these are too easy compare to the exam. This is a good starting place though.</li>
  <li><a href="https://www.exampro.co/github-actions">Exam Pro</a></li>
</ul>

<p>The ExamPro videos are free on YouTube, and if you want more structured practice, you can buy their flashcards and 3–4 full practice exams for around <strong>$30</strong>. For me, that was well worth it.</p>

<h2 id="what-to-expect-on-the-exam">What to Expect on the Exam</h2>

<p>One thing you <em>really</em> need to know about this exam: <strong>questions compound knowledge</strong>.</p>

<p>You’ll often see an answer that looks like the obvious, quick win. But it’s not the right one.</p>

<p>Slow down. Read carefully. There’s usually an additional constraint, feature, or enterprise-level detail baked into the question that changes the answer.</p>

<p>I also heard from multiple coworkers that the <strong>enterprise features</strong> caught them off guard. Because I had that heads-up going in, that section didn’t trip me up. If you’re studying, don’t skip enterprise topics. They matter.</p>

<h2 id="results--timeline">Results &amp; Timeline</h2>

<ul>
  <li><strong>Score:</strong> 890 / 1000</li>
  <li>
    <p><strong>Study time:</strong> ~2–3 weeks</p>

    <ul>
      <li>1–2 hours after work on weekdays</li>
      <li>A few hands-on labs on the weekends</li>
    </ul>
  </li>
</ul>

<p>I already had a background in <strong>Azure DevOps</strong>, which made the GitHub Actions content easier to digest, but this exam is still very doable if you’re consistent.</p>

<h2 id="the-cheat-sheet">The Cheat Sheet</h2>

<p>Below is the cheat sheet I put together to review right before the exam.</p>

<h2 id="domain-1-author-and-maintain-workflows-40">Domain 1: Author and Maintain Workflows (40%)</h2>

<h3 id="workflow-triggers-events">Workflow Triggers (Events)</h3>

<p><strong>Multiple Events</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">push</span><span class="pi">,</span> <span class="nv">pull_request</span><span class="pi">]</span>
</code></pre></div></div>

<p><strong>Scheduled (Cron)</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span>
  <span class="na">schedule</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">2</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">1"</span> <span class="c1"># Mondays at 2am UTC</span>
</code></pre></div></div>

<p><strong>Manual Trigger</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="na">environment</span><span class="pi">:</span>
        <span class="na">required</span><span class="pi">:</span> <span class="no">true</span>
</code></pre></div></div>

<p><strong>Webhook Events</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">check_run</code></li>
  <li><code class="language-plaintext highlighter-rouge">check_suite</code></li>
  <li><code class="language-plaintext highlighter-rouge">deployment</code></li>
  <li><code class="language-plaintext highlighter-rouge">deployment_status</code></li>
  <li>Used for CI/CD integrations and chained workflows</li>
</ul>

<h3 id="workflow-components">Workflow Components</h3>

<table>
  <thead>
    <tr>
      <th>Component</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Workflow</td>
      <td>YAML file in <code class="language-plaintext highlighter-rouge">.github/workflows/</code></td>
    </tr>
    <tr>
      <td>Job</td>
      <td>Group of steps, runs on one runner</td>
    </tr>
    <tr>
      <td>Step</td>
      <td>Action or shell command</td>
    </tr>
    <tr>
      <td>Action</td>
      <td>Reusable unit (JS, Docker, composite)</td>
    </tr>
    <tr>
      <td>Run</td>
      <td>One execution of a workflow</td>
    </tr>
  </tbody>
</table>

<h3 id="workflow-syntax-basics">Workflow Syntax Basics</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
      <span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="s">npm install</span>
</code></pre></div></div>

<p><strong>Key Rules</strong></p>

<ul>
  <li>YAML indentation matters</li>
  <li><code class="language-plaintext highlighter-rouge">steps</code> must be under <code class="language-plaintext highlighter-rouge">jobs.&lt;job_id&gt;</code></li>
  <li>Each job runs on a single runner</li>
</ul>

<h3 id="conditional-logic">Conditional Logic</h3>

<p><strong>Step Condition</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">if</span><span class="pi">:</span> <span class="s">github.ref == 'refs/heads/main'</span>
</code></pre></div></div>

<p><strong>Job Condition</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">if</span><span class="pi">:</span> <span class="s">success()</span>
</code></pre></div></div>

<h3 id="dependent-jobs">Dependent Jobs</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="s">build</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
</code></pre></div></div>

<h3 id="runners">Runners</h3>

<p><strong>GitHub-hosted</strong></p>

<ul>
  <li>Short-lived</li>
  <li>No custom software</li>
  <li>Best for standard CI</li>
</ul>

<p><strong>Self-hosted</strong></p>

<ul>
  <li>Custom tools</li>
  <li>Network access</li>
  <li>Long-running jobs</li>
</ul>

<p><strong>Runner Labels</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">runs-on</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">self-hosted</span><span class="pi">,</span> <span class="nv">linux</span><span class="pi">,</span> <span class="nv">custom-software</span><span class="pi">]</span>
</code></pre></div></div>

<h3 id="secrets--environment-variables">Secrets &amp; Environment Variables</h3>

<p><strong>Encrypted Secrets</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">env</span><span class="pi">:</span>
  <span class="na">API_KEY</span><span class="pi">:</span> <span class="s">$</span>
</code></pre></div></div>

<p><strong>Default Environment Variables</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">GITHUB_REPOSITORY</code></li>
  <li><code class="language-plaintext highlighter-rouge">GITHUB_REF</code></li>
  <li><code class="language-plaintext highlighter-rouge">GITHUB_SHA</code></li>
  <li><code class="language-plaintext highlighter-rouge">GITHUB_RUN_ID</code></li>
</ul>

<p><strong>Custom Env Vars</strong></p>

<ul>
  <li>Workflow-level: <code class="language-plaintext highlighter-rouge">env:</code></li>
  <li>Job-level: <code class="language-plaintext highlighter-rouge">jobs.&lt;job&gt;.env</code></li>
  <li>Step-level: <code class="language-plaintext highlighter-rouge">steps[].env</code></li>
</ul>

<h3 id="github_token">GITHUB_TOKEN</h3>

<p>Use when:</p>

<ul>
  <li>Creating issues</li>
  <li>Publishing packages</li>
  <li>Pushing to repos</li>
  <li>Authenticating to GitHub APIs</li>
</ul>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">env</span><span class="pi">:</span>
  <span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
</code></pre></div></div>

<h3 id="workflow-commands">Workflow Commands</h3>

<p><strong>Set Environment Variable</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"MY_VAR=value"</span> <span class="o">&gt;&gt;</span> <span class="nv">$GITHUB_ENV</span>
</code></pre></div></div>

<p><strong>Set Output</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"result=success"</span> <span class="o">&gt;&gt;</span> <span class="nv">$GITHUB_OUTPUT</span>
</code></pre></div></div>

<h3 id="publishing--deployment">Publishing &amp; Deployment</h3>

<p><strong>GitHub Packages / GHCR</strong></p>

<ul>
  <li>Authenticate</li>
  <li>Build artifact/image</li>
  <li>Push to registry</li>
</ul>

<p><strong>Service Containers</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
  <span class="na">postgres</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">postgres:15</span>
</code></pre></div></div>

<p><strong>CodeQL</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">github/codeql-action/analyze@v3</span>
</code></pre></div></div>

<p><strong>Releases</strong></p>

<ul>
  <li>Create GitHub release</li>
  <li>Upload artifacts</li>
  <li>Deploy to cloud provider</li>
</ul>

<h2 id="domain-2-consume-workflows-20">Domain 2: Consume Workflows (20%)</h2>

<h3 id="interpreting-workflow-effects">Interpreting Workflow Effects</h3>

<ul>
  <li>Check commits, PRs, releases, issues</li>
  <li>Inspect triggered event via run metadata</li>
  <li>Read workflow YAML to infer behavior</li>
</ul>

<h3 id="diagnosing-failures">Diagnosing Failures</h3>

<p><strong>UI</strong></p>

<ul>
  <li>Actions tab → Workflow → Job → Step logs</li>
</ul>

<p><strong>REST API</strong></p>

<ul>
  <li>List workflow runs</li>
  <li>Download logs and artifacts</li>
</ul>

<h3 id="debugging">Debugging</h3>

<p><strong>Enable Step Debug</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">env</span><span class="pi">:</span>
  <span class="na">ACTIONS_STEP_DEBUG</span><span class="pi">:</span> <span class="no">true</span>
</code></pre></div></div>

<h3 id="data-between-jobs">Data Between Jobs</h3>

<p><strong>Artifacts</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-artifact@v4</span>
</code></pre></div></div>

<p><strong>Outputs</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">outputs</span><span class="pi">:</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">$</span>
</code></pre></div></div>

<h3 id="caching">Caching</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache@v4</span>
  <span class="na">with</span><span class="pi">:</span>
    <span class="na">path</span><span class="pi">:</span> <span class="s">~/.npm</span>
    <span class="na">key</span><span class="pi">:</span> <span class="s">npm-${{ hashFiles('package-lock.json') }}</span>
</code></pre></div></div>

<h3 id="workflow-management">Workflow Management</h3>

<ul>
  <li>Add status badge from Actions UI</li>
  <li>
    <p>Delete vs Disable:</p>

    <ul>
      <li><strong>Disable:</strong> keeps history</li>
      <li><strong>Delete:</strong> removes file + history</li>
    </ul>
  </li>
  <li>
    <p>Environment protections:</p>

    <ul>
      <li>Required reviewers</li>
      <li>Deployment gates</li>
    </ul>
  </li>
  <li>Matrix builds:</li>
</ul>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">strategy</span><span class="pi">:</span>
  <span class="na">matrix</span><span class="pi">:</span>
    <span class="na">node</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">18</span><span class="pi">,</span> <span class="nv">20</span><span class="pi">]</span>
</code></pre></div></div>

<h2 id="domain-3-author-and-maintain-actions-25">Domain 3: Author and Maintain Actions (25%)</h2>

<h3 id="action-types">Action Types</h3>

<table>
  <thead>
    <tr>
      <th>Type</th>
      <th>Use Case</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>JavaScript</td>
      <td>Fast, reusable, Node-based</td>
    </tr>
    <tr>
      <td>Docker</td>
      <td>Heavy dependencies</td>
    </tr>
    <tr>
      <td>Composite</td>
      <td>Glue steps together</td>
    </tr>
  </tbody>
</table>

<h3 id="action-structure">Action Structure</h3>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>action.yml
index.js
Dockerfile
</code></pre></div></div>

<p><strong>Metadata</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">name</code></li>
  <li><code class="language-plaintext highlighter-rouge">inputs</code></li>
  <li><code class="language-plaintext highlighter-rouge">runs</code></li>
  <li><code class="language-plaintext highlighter-rouge">outputs</code></li>
</ul>

<h3 id="troubleshooting-actions">Troubleshooting Actions</h3>

<p><strong>JavaScript</strong></p>

<ul>
  <li>Check Node version</li>
  <li>Review logs</li>
  <li>Validate <code class="language-plaintext highlighter-rouge">action.yml</code></li>
</ul>

<p><strong>Docker</strong></p>

<ul>
  <li>Dockerfile errors</li>
  <li>ENTRYPOINT issues</li>
  <li>Permissions</li>
</ul>

<h3 id="workflow-commands-in-actions">Workflow Commands in Actions</h3>

<ul>
  <li>Exit codes control success/failure</li>
  <li><code class="language-plaintext highlighter-rouge">core.setFailed()</code></li>
  <li><code class="language-plaintext highlighter-rouge">process.exit(1)</code></li>
</ul>

<h2 id="domain-4-manage-github-actions-in-the-enterprise-15">Domain 4: Manage GitHub Actions in the Enterprise (15%)</h2>

<h3 id="reuse--distribution">Reuse &amp; Distribution</h3>

<ul>
  <li>Reusable workflows (<code class="language-plaintext highlighter-rouge">workflow_call</code>)</li>
  <li>Centralized action repos</li>
  <li>Naming conventions</li>
  <li>Version pinning</li>
</ul>

<h3 id="access-control">Access Control</h3>

<ul>
  <li>Organization policies</li>
  <li>Allow/deny action sources</li>
  <li>Control who can use which actions</li>
</ul>

<h3 id="runners-at-scale">Runners at Scale</h3>

<p><strong>GitHub-hosted</strong></p>

<ul>
  <li>Subject to IP allow lists</li>
</ul>

<p><strong>Self-hosted</strong></p>

<ul>
  <li>Custom networking</li>
  <li>Proxies</li>
  <li>Labels</li>
  <li>Groups for access control</li>
</ul>

<h3 id="runner-management">Runner Management</h3>

<ul>
  <li>Group runners by purpose</li>
  <li>Move runners between groups</li>
  <li>Monitor <code class="language-plaintext highlighter-rouge">_diag</code> logs</li>
  <li>Update runner binaries regularly</li>
</ul>

<h3 id="secrets-in-the-enterprise">Secrets in the Enterprise</h3>

<table>
  <thead>
    <tr>
      <th>Scope</th>
      <th>Use</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Organization</td>
      <td>Shared across repos</td>
    </tr>
    <tr>
      <td>Repository</td>
      <td>Repo-specific</td>
    </tr>
    <tr>
      <td>Environment</td>
      <td>Protected deployments</td>
    </tr>
  </tbody>
</table>

<p>Access via:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="s">${{ secrets.MY_SECRET }}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="github" /><summary type="html"><![CDATA[I haven’t blogged in a bit, and my home lab has definitely taken a back seat lately. The reason? I’ve been heads-down on certifications.]]></summary></entry><entry><title type="html">My First Project with GitHub Spec Kit and Spec-Driven Development</title><link href="https://rswitzer.github.io/spec-kit/2025/12/22/spec-kit.html" rel="alternate" type="text/html" title="My First Project with GitHub Spec Kit and Spec-Driven Development" /><published>2025-12-22T00:00:00+00:00</published><updated>2025-12-22T00:00:00+00:00</updated><id>https://rswitzer.github.io/spec-kit/2025/12/22/spec-kit</id><content type="html" xml:base="https://rswitzer.github.io/spec-kit/2025/12/22/spec-kit.html"><![CDATA[<p>In my previous posts, I’ve touched on the “Iterative Approach” to AI development is a must. And, if you don’t create guardrails, you get garbage. It is that simple. This is why I’ve moved toward <strong>Spec-Driven Development (SDD)</strong>—a methodology that enforces structure before a single line of code is written.</p>

<p>Today, I’m putting this into practice using <a href="https://github.com/github/spec-kit">GitHub Spec Kit</a> to build the foundation of my latest personal project.</p>

<hr />

<h2 id="what-is-spec-driven-development">What is Spec-Driven Development?</h2>

<p>Spec-Driven Development is a workflow where the requirements (the “Spec”) are the source of truth. Instead of asking an AI to “build an app,” you use it to help you define a <strong>Constitution</strong>, a <strong>Specification</strong>, and a <strong>Plan</strong> as guardrails. Only then do you move to execution.</p>

<p>GitHub Spec Kit is a relatively new framework (released in late 2024/early 2025) designed to streamline this within GitHub Copilot. For this session, I used <strong>GitHub Copilot with GPT-5</strong> for the “Architect” phase (drafting the specs) and switched to <strong>Claude Opus 4.5</strong> for the “Developer” phase (the execution).</p>

<hr />

<h2 id="phase-1-the-constitution">Phase 1: The Constitution</h2>

<p>In Spec Kit, the <strong>Constitution</strong> is the soul of your project. It defines your “laws”—tech stack, coding standards, and architectural constraints. It ensures the AI doesn’t hallucinate a random library or a different language halfway through the build.</p>

<p>I’m building a .NET 10 backend with a React/Tailwind frontend, targeting a Raspberry Pi 5 Kubernetes cluster. Here is the prompt I used to establish my rules:</p>

<blockquote>
  <p><strong>Prompt:</strong> <code class="language-plaintext highlighter-rouge">/speckit.constitution I want to build consitution for my application using the following technologies. Frontend: React, Tailwind CSS, Playwright (E2E). Backend: .NET 10, xUnit + FluentAssertions, WebApplicationFactory (integration tests). Platform: Kubernetes, Docker (ARM64-compatible images), Raspberry Pi 5 (initial target). Portable to major cloud providers. CI/CD &amp; Ops: GitHub Actions (self-hosted runner), Dependabot, OpenTelemetry, Prometheus + Grafana. Before ever pulling in new technology consult me first.</code></p>
</blockquote>

<p><strong>The Result:</strong>
<a href="https://github.com/rswitzer/etsy-optimizer/blob/bef4e399cfb166961ce99259f14709ec2505b7b0/specs/.specify/memory/constitution.md">constitution.md</a></p>

<hr />

<h2 id="phase-2-the-specification">Phase 2: The Specification</h2>

<p>With the laws set, I needed to <strong>Specify</strong>. I wanted a “Hello World” API—something small to validate my K8s infrastructure so I can hit the ground running when my Raspberry Pi’s arrive later this wek.</p>

<blockquote>
  <p><strong>Prompt:</strong> <code class="language-plaintext highlighter-rouge">/speckit.specify I want to build a simple hello world application that I can I use for deploying and validating my kubernents cluster, which will be built later on.</code></p>
</blockquote>

<p><strong>The Result:</strong>
<a href="https://github.com/rswitzer/etsy-optimizer/blob/bef4e399cfb166961ce99259f14709ec2505b7b0/specs/.specify/hello-world.spec.md">hello-world.spec.md</a></p>

<hr />

<h2 id="phase-3-the-plan--tasks">Phase 3: The Plan &amp; Tasks</h2>

<p>If the Spec defines <em>what</em> must exist, the <strong>Plan</strong> defines <em>how</em> it will be built. Since I wanted to see how well the LLM handled the Spec Kit workflow, I let it take the lead on the roadmap.</p>

<blockquote>
  <p><strong>Prompt:</strong> <code class="language-plaintext highlighter-rouge">/speckit.plan create a plan based of the constitution and spec.</code></p>
</blockquote>

<p>This generated the execution roadmap under <code class="language-plaintext highlighter-rouge">hello-world-k8s-validation</code> folder. To make it actionable, I immediately followed up with:</p>

<blockquote>
  <p><strong>Prompt:</strong> <code class="language-plaintext highlighter-rouge">/speckit.tasks create tasks</code></p>
</blockquote>

<p>This produced a <a href="https://github.com/rswitzer/etsy-optimizer/blob/bef4e399cfb166961ce99259f14709ec2505b7b0/specs/hello-world-k8s-validation/tasks.md">tasks.md</a> file—a checklist of manageable units of work that the AI (or a human) can follow to complete the feature.</p>

<hr />

<h2 id="phase-4-execution-with-claude-opus-45">Phase 4: Execution with Claude Opus 4.5</h2>

<p>This is where I switched gears. While GPT-5 handled the architectural “thinking” beautifully, I wanted to see <strong>Claude Opus 4.5</strong> handle the “doing.”</p>

<blockquote>
  <p><strong>Prompt:</strong> <code class="language-plaintext highlighter-rouge">implement the tasks for this project</code></p>
</blockquote>

<p>The magic of Spec Kit is that because the <code class="language-plaintext highlighter-rouge">tasks.md</code> and <code class="language-plaintext highlighter-rouge">constitution.md</code> were so well-defined, Claude didn’t have to guess. It implemented the hello-world endpoints, set up the OpenTelemetry logging, and generated the Dockerfiles for ARM64 (Raspberry Pi) compatibility without a hitch.</p>

<p>Before finishing, I realized I needed a <code class="language-plaintext highlighter-rouge">.gitignore</code> to keep things clean:</p>

<blockquote>
  <p><strong>Prompt:</strong> <code class="language-plaintext highlighter-rouge">create a .gitignore based on this project and git best practices</code></p>
</blockquote>

<hr />

<h2 id="final-thoughts">Final Thoughts</h2>

<p>The entire session—from reading the Spec Kit docs to having a deployable, containerized .NET 10 app with full observability—took only <strong>30 minutes</strong>.</p>

<p>I can’t test the infrastructure on the actual Raspberry Pi 5s yet (they are still in transit), but the code is reviewed, test pass and the telemetry is validated. Later this week, I’ll be automating the Pi setup with Ansible and a self-hosted GitHub runner. I will be able to test the rest out then.</p>

<p><strong>Full Session Commit:</strong>
<a href="https://github.com/rswitzer/etsy-optimizer/commit/bef4e399cfb166961ce99259f14709ec2505b7b0">https://github.com/rswitzer/etsy-optimizer/commit/bef4e399cfb166961ce99259f14709ec2505b7b0</a></p>]]></content><author><name></name></author><category term="spec-kit" /><summary type="html"><![CDATA[In my previous posts, I’ve touched on the “Iterative Approach” to AI development is a must. And, if you don’t create guardrails, you get garbage. It is that simple. This is why I’ve moved toward Spec-Driven Development (SDD)—a methodology that enforces structure before a single line of code is written.]]></summary></entry><entry><title type="html">Day 1 with Agentic AI: Bringing GPT-5.1 Codex + Copilot Into My Workflow</title><link href="https://rswitzer.github.io/2025/12/07/gpt-51-codex.html" rel="alternate" type="text/html" title="Day 1 with Agentic AI: Bringing GPT-5.1 Codex + Copilot Into My Workflow" /><published>2025-12-07T00:00:00+00:00</published><updated>2025-12-07T00:00:00+00:00</updated><id>https://rswitzer.github.io/2025/12/07/gpt-51-codex</id><content type="html" xml:base="https://rswitzer.github.io/2025/12/07/gpt-51-codex.html"><![CDATA[<p>My client stakeholder recently asked for an overview of what it would take to bring <strong>GPT-5.1 Codex</strong> into their engineering organization, along with a clearer understanding of its capabilities. Instead of producing yet another slide deck, I decided to answer the question the way I approach real engineering work: by building something.</p>

<p>And that’s how I ended up spending Day 1 of my upskilling project using <strong>GitHub Copilot Pro</strong>, <strong>VS Code</strong>, and <strong>GPT-5.1-codex</strong> to scaffold an actual application.</p>

<p>As a crafter who makes jewelry (<a href="https://www.instagram.com/rachelswitzerjewelry/">Instagram</a> and <a href="https://www.etsy.com/shop/rachelswitzerjewelry">Etsy</a>), I’ve got a backlog of pieces waiting to be listed. So why not build something useful? One of my upcoming side projects is an app that takes in a photo and generates an SEO-optimized Etsy listing. Perfect playground. Perfect excuse to explore where software engineering is going with agentic AI.</p>

<hr />

<h2 id="why-im-doing-this-now">Why I’m Doing This Now</h2>

<p>I’ve spent the last few years operating at the architecture level, so I’m intentionally pulling myself closer to implementation again. I fully intended to start with Kubernetes work, but the timing of the client request plus the personal project opportunity made GPT-5.1 the right place to begin.</p>

<p>To properly evaluate the tooling, I upgraded to <strong>GitHub Copilot Pro for Individuals</strong>. The free tier doesn’t include GPT-5.1-codex, and Pro comes with <strong>300 premium requests</strong> per month.</p>

<p>A premium request is essentially a unit of consumption for advanced Copilot features. The quick summary:</p>

<h3 id="how-premium-requests-are-calculated">How Premium Requests Are Calculated</h3>

<ul>
  <li><strong>Base request unit:</strong> 1 premium request</li>
  <li>Applies to things like:
    <ul>
      <li>Copilot Chat prompts</li>
      <li>Copilot Code Reviews</li>
      <li>Copilot Coding Agent tasks such as creating PRs</li>
    </ul>
  </li>
  <li>A model multiplier is applied depending on which Copilot model is used, but GPT-5.1 Codex is the primary one I’m testing here.</li>
</ul>

<hr />

<h2 id="starting-the-experiment-lets-build-something">Starting the Experiment: Let’s Build Something</h2>

<p>My first instinct was to dive right into my Etsy listing generator idea. Codex generated a lot of code very quickly, but I couldn’t get the project running. That’s on me – this is a new laptop I haven’t fully configured for development yet.</p>

<p>I realized that before evaluating Copilot as an engineering partner, I needed a known baseline. So I reset and started smaller.</p>

<h3 id="prompt-1">Prompt 1:</h3>
<blockquote>
  <p>“Create a .NET Core app with a health check page.”</p>
</blockquote>

<p>Great starting point. A predictable output. Easy to validate. And a chance to assess whether the code felt trustworthy.</p>

<p>Copilot scaffolded a full application with multiple pages, including a weather endpoint I didn’t ask for. I prompted it to remove that. It did.</p>

<p>I hit debug and boom: <strong>healthy environment, healthy endpoint</strong>.</p>

<hr />

<h2 id="refactoring-toward-best-practices">Refactoring Toward Best Practices</h2>

<p>Once I knew my dev environment was working, I asked Codex to take the next step.</p>

<h3 id="prompt-2">Prompt 2:</h3>
<blockquote>
  <p>“Refactor this into a production-ready .NET Core application.<br />
Keep only the health check endpoint.<br />
Add a placeholder API for <code class="language-plaintext highlighter-rouge">Listing Builder</code>.<br />
Add unit tests.<br />
Use best practices.”</p>
</blockquote>

<p>This is where the agentic behavior kicked in.</p>

<p>It created new projects, reorganized folders, scaffolded tests, deleted unnecessary files, attempted builds, and prompted me to allow command-line execution to update the solution structure. I could <em>see</em> the AI thinking and iterating.</p>

<p>When it finished, it produced a clean, professional-looking solution layout.</p>

<p>I briefly reviewed the code. Everything made sense. It read like something a senior engineer would produce. The unit tests passed. The app built. The health endpoint ran.</p>

<p>Now I have a solid foundation for the rest of the project.</p>

<hr />

<h2 id="the-final-touch-gitignore-and-initial-commit">The Final Touch: GitIgnore and Initial Commit</h2>

<p>Before wrapping Day 1, I asked it to configure a proper <code class="language-plaintext highlighter-rouge">.gitignore</code>, then pushed the working scaffold to GitHub so I can pick this back up later in the week and begin building the UI, cleaning the code uo further and wiring up the backend.</p>

<p><strong><a href="https://github.com/rswitzer/etsy-ai-generator/commit/5611f7502298893d9956a8ff8af7dc4539f4affd">Commit here</a></strong></p>

<hr />

<h2 id="early-takeaways-after-day-1">Early Takeaways After Day 1</h2>

<p><strong>1. Prompt-first development is real.</strong><br />
The more intentional and incremental your prompts, the clearer and more predictable the output.</p>

<p><strong>2. Reading code matters more than writing it.</strong><br />
Copilot can generate astonishing amounts of code. Your value is increasingly in reviewing, shaping, and validating it.</p>

<p><strong>3. Agentic workflows feel like pairing with a senior engineer.</strong><br />
It scaffolds, refactors, reorganizes, builds, and tests – not just a text generator, but an active engineering partner.</p>

<p><strong>4. The productivity boost is no joke.</strong><br />
What normally takes 1–2 hours took <strong>about 10 minutes</strong>.</p>

<p>I haven’t even touched the actual Etsy listing logic yet, but the foundation is there. And I can already see how this will reshape how engineers and architects build software.</p>

<p>This was just <strong>Day 1</strong> with GPT-5.1 Codex in agent mode. I’m excited to see how far I can push it next.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[My client stakeholder recently asked for an overview of what it would take to bring GPT-5.1 Codex into their engineering organization, along with a clearer understanding of its capabilities. Instead of producing yet another slide deck, I decided to answer the question the way I approach real engineering work: by building something.]]></summary></entry><entry><title type="html">Content Over Convention: How Generative AI Helps Me Ship Technical Thoughts</title><link href="https://rswitzer.github.io/genai,/blogging/2025/11/30/blogging-and-genai.html" rel="alternate" type="text/html" title="Content Over Convention: How Generative AI Helps Me Ship Technical Thoughts" /><published>2025-11-30T00:00:00+00:00</published><updated>2025-11-30T00:00:00+00:00</updated><id>https://rswitzer.github.io/genai,/blogging/2025/11/30/blogging-and-genai</id><content type="html" xml:base="https://rswitzer.github.io/genai,/blogging/2025/11/30/blogging-and-genai.html"><![CDATA[<p>For this blog, Generative AI has become my primary co-pilot, handling the tasks that prevent me from focusing on the technical content.</p>

<h3 id="where-ai-does-the-job-abstracting-the-friction">Where AI Does the Job: Abstracting the Friction</h3>

<p>I use GenAI to eliminate necessary but non-enjoyable tasks that cause friction:</p>

<ul>
  <li><strong>Aesthetics for “Good Enough”:</strong> I use <strong>ChatGPT</strong> to generate header images simply by describing what I want. I have no desire to spend time in Photoshop. The goal is a <strong>“good enough”</strong> aesthetic so that my focus remains entirely on the technical message.</li>
  <li><strong>Content Structuring (The Ramble-to-Organize Method):</strong> This is the biggest win. I’ll be frank: I hate writing and organizing my thoughts. Now, I bypass that friction entirely:
    <ul>
      <li>I open a prompt in <strong>ChatGPT, Copilot, or Gemini</strong> and just start <strong>“talking”</strong> out every stream-of-consciousness thought I have.</li>
      <li>I treat it like expressing my thoughts to a friend.</li>
      <li>The AI then converts that messy, rambling stream into an organized, coherent, and grammatically sound blog post structure.</li>
    </ul>
  </li>
</ul>

<p>GenAI allows me to <strong>“get my words out”</strong> quickly, ensuring my energy is spent on the core message, not the mechanics of writing.</p>

<h3 id="where-i-draw-the-line-preserving-the-creative-process">Where I Draw the Line: Preserving the Creative Process</h3>

<p>While I embrace GenAI for functional efficiency, I maintain a firm, non-negotiable boundary: <strong>I do not use GenAI in my personal arts or crafts.</strong></p>

<p>I believe there is still a special, beautiful quality in something created from the human mind and hands. I love the creative process itself—the friction, the experimentation, and the hands-on effort. There is value and beauty in that. For me, the value of this blog is in the technical experience, so I use GenAI to protect my time for <em>that</em>, but I deliberately choose to avoid it in my personal creative pursuits where the process itself is the reward.</p>]]></content><author><name></name></author><category term="GenAI," /><category term="Blogging" /><summary type="html"><![CDATA[For this blog, Generative AI has become my primary co-pilot, handling the tasks that prevent me from focusing on the technical content.]]></summary></entry><entry><title type="html">Beyond the Quickstart: My Tips for Building a Stable Technical Blog on Jekyll and GitHub Pages</title><link href="https://rswitzer.github.io/jekyll,/github/pages/2025/11/29/building-blog-on-github-pages.html" rel="alternate" type="text/html" title="Beyond the Quickstart: My Tips for Building a Stable Technical Blog on Jekyll and GitHub Pages" /><published>2025-11-29T00:00:00+00:00</published><updated>2025-11-29T00:00:00+00:00</updated><id>https://rswitzer.github.io/jekyll,/github/pages/2025/11/29/building-blog-on-github-pages</id><content type="html" xml:base="https://rswitzer.github.io/jekyll,/github/pages/2025/11/29/building-blog-on-github-pages.html"><![CDATA[<h2 id="the-stack-github-pages-and-jekyll"><strong>The Stack: GitHub Pages and Jekyll</strong></h2>

<p><strong>GitHub Pages</strong> is a free static site hosting service that publishes a site directly from your GitHub repository.
<strong>Jekyll</strong> is the static site generator used by GitHub Pages. It converts Markdown, Liquid templates, and theme files into a complete static HTML website.</p>

<p>Because I’m using GitHub Pages’ <strong>built-in Jekyll build</strong>, it runs in <strong>safe mode</strong>, which means:</p>

<ul>
  <li>only approved plugins are allowed</li>
  <li>gem-based themes work normally</li>
  <li>remote themes are supported</li>
  <li>no custom build pipeline is needed</li>
</ul>

<p>This is perfect for a lightweight, low-maintenance technical blog.</p>

<h2 id="tip-customizing-your-theme"><strong>Tip: Customizing Your Theme</strong></h2>

<p>I followed the official <a href="https://docs.github.com/en/pages/quickstart">GitHub Pages Quickstart</a>, but I hit a few common friction points once I started customizing.</p>

<h3 id="1-theme-compatibility-issues"><strong>1. Theme Compatibility Issues</strong></h3>

<p>Each Jekyll theme expects a specific file structure. Switching themes often produces errors about missing layout files or front matter, because themes define different required pages and partials.</p>

<h3 id="2-hidden-theme-source"><strong>2. Hidden Theme Source</strong></h3>

<p>Most Jekyll themes are shipped as Ruby gems. Their internal files — layouts, includes, Sass — are <strong>not visible in your repository</strong>, which means you cannot directly edit things like:</p>

<ul>
  <li>header layout</li>
  <li>navigation partials</li>
  <li>page templates</li>
</ul>

<h3 id="the-customization-fix"><strong>The Customization Fix</strong></h3>

<p>To customize deeper than configuration alone allows, you simply override the theme files locally.</p>

<p>The process is straightforward:</p>

<ol>
  <li>Visit the theme’s GitHub source repository</li>
  <li>Download the parts you want to customize</li>
  <li>
    <p>Copy them into your own repo under:</p>

    <ul>
      <li><code class="language-plaintext highlighter-rouge">_layouts/</code></li>
      <li><code class="language-plaintext highlighter-rouge">_includes/</code></li>
      <li><code class="language-plaintext highlighter-rouge">_sass/</code></li>
    </ul>
  </li>
</ol>

<p>Any file placed in your repository automatically overrides the version inside the theme gem. No forking or custom builds needed.</p>

<h2 id="choosing-stability-locking-down-the-remote-theme"><strong>Choosing Stability: Locking Down the Remote Theme</strong></h2>

<p>I ended up sticking with the default <strong>Minima</strong> theme but switched to the dark variant. Because GitHub Pages supports remote themes, you can reference a theme repository directly in your <code class="language-plaintext highlighter-rouge">_config.yml</code>.</p>

<p>But here’s the architect’s trick for long-term stability:</p>

<h3 id="pin-the-remote-theme-to-a-specific-commit"><strong>Pin the remote theme to a specific commit.</strong></h3>

<p>Instead of referencing the theme repo like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>remote_theme: owner/repo
</code></pre></div></div>

<p>you pin it like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>remote_theme: owner/repo@&lt;commit-hash&gt;
</code></pre></div></div>

<p>This ensures:</p>

<ul>
  <li>no unexpected theme updates</li>
  <li>no surprise build breaks</li>
  <li>consistent layout and styling over time</li>
</ul>

<p>This works <strong>even without GitHub Actions</strong>, because GitHub Pages allows remote themes — and allows pinning them — as long as they are GitHub Pages–compatible.</p>

<p><em>Note: I will eventually give the GitHub Actions deployment a try.</em></p>]]></content><author><name></name></author><category term="Jekyll," /><category term="GitHub" /><category term="Pages" /><summary type="html"><![CDATA[The Stack: GitHub Pages and Jekyll]]></summary></entry><entry><title type="html">Why I Blog: My Three Strategic Goals as a Technical Architect</title><link href="https://rswitzer.github.io/architect/2025/11/28/architecture-goals.html" rel="alternate" type="text/html" title="Why I Blog: My Three Strategic Goals as a Technical Architect" /><published>2025-11-28T00:00:00+00:00</published><updated>2025-11-28T00:00:00+00:00</updated><id>https://rswitzer.github.io/architect/2025/11/28/architecture-goals</id><content type="html" xml:base="https://rswitzer.github.io/architect/2025/11/28/architecture-goals.html"><![CDATA[<p>This blog is more than just a writing exercise; it is a commitment to a set of core principles that define my philosophy as a technical leader. Everything I publish here is in pursuit of three strategic goals.</p>

<p>This is the <strong>“Why”</strong> that drives me to carve out time for hands-on work and share my experiences:</p>

<ol>
  <li>
    <h3 id="reconnecting-architects-to-implementation">Reconnecting Architects to Implementation</h3>
    <p>We are former engineers who love solving problems, but years in the architecture space can lead to abstraction from implementation details. This blog is my way of figuring out and showing other architects how to stay hands-on.</p>
    <ul>
      <li><strong>The Benefit:</strong> Going through the implementation process is the crucial step in evaluating technology. It quickly reveals where marketing hype ends and real-world complexity begins, ensuring our decisions are practical and ultimately making us better architects.</li>
    </ul>
  </li>
  <li>
    <h3 id="career-accountability-and-marketability">Career Accountability and Marketability</h3>
    <p>The GenAI revolution is rapidly making the technical market competitive. This blog serves as a mechanism to:</p>
    <ul>
      <li><strong>Keep Myself Accountable:</strong> It is a public commitment to continuous learning and upskilling in my career.</li>
      <li><strong>Showcase My Knowledge:</strong> It allows me to build a portfolio that demonstrates what I can do and how I think, helping me maintain my competitive edge.</li>
    </ul>
  </li>
  <li>
    <h3 id="making-engineers-lives-easier">Making Engineers’ Lives Easier</h3>
    <p>As a technical leader, my responsibility is to reduce friction and set my engineers up for success.</p>
    <ul>
      <li><strong>Empathy Through Experience:</strong> Being hands-on during implementation is critical for building empathy for the engineering team.</li>
      <li><strong>Responsible Leadership:</strong> Making good decisions with technology, and using tools like GenAI responsibly, is how we reduce mental load and stress, leading to happier, more successful engineers and teams.</li>
    </ul>
  </li>
</ol>

<p>This blog is my commitment to bridging the gap between high-level strategy and low-level code, ensuring that every architectural decision I make is informed by current, real-world development experience.</p>]]></content><author><name></name></author><category term="Architect" /><summary type="html"><![CDATA[This blog is more than just a writing exercise; it is a commitment to a set of core principles that define my philosophy as a technical leader. Everything I publish here is in pursuit of three strategic goals.]]></summary></entry><entry><title type="html">Hello World! Rediscovering Hands-On Engineering: My Upskilling Journey</title><link href="https://rswitzer.github.io/2025/11/26/hello-world.html" rel="alternate" type="text/html" title="Hello World! Rediscovering Hands-On Engineering: My Upskilling Journey" /><published>2025-11-26T00:00:00+00:00</published><updated>2025-11-26T00:00:00+00:00</updated><id>https://rswitzer.github.io/2025/11/26/hello-world</id><content type="html" xml:base="https://rswitzer.github.io/2025/11/26/hello-world.html"><![CDATA[<p>For the past three years, my career has revolved around technical project leadership. I’ve been in meetings, guiding engineers, and making architectural recommendations based largely on documentation. It’s the role many architects find themselves in—strategic, high-level, but increasingly disconnected from the hands-on work that originally drew us into technology.</p>

<p>And here’s the truth: I’m rusty.</p>

<p>When I was an individual contributor, I often questioned architectural decisions. <em>Did this architect ever even touch the technology they’re recommending?</em> I remember the early days of Xamarin—marketed as revolutionary, but in practice it was clunky, unstable, and frustrating. I spent countless hours just trying to get builds to work locally. That experience left me wishing architects had more firsthand knowledge of the tools they were prescribing.</p>

<p>Now, standing on the other side of the table, I understand why. Architects are buried in meetings, juggling deadlines, and rarely have time for proof-of-concepts. Staying current often means sacrificing personal time, and after eight hours of calls and coordination, the thought of diving into new tech can feel exhausting.</p>

<p>But I don’t want to lose touch.</p>

<h2 id="my-plan-learning-by-building">My Plan: Learning by Building</h2>

<p>This blog will document my journey back into hands-on engineering. My goal is to find simple, meaningful ways to stay up to date—projects that tie into my passions outside of work, so learning doesn’t feel like yet another obligation.</p>

<p>Here’s what I’m setting out to do:</p>

<ul>
  <li>
    <p><strong>Kubernetes Raspberry Pi Cluster</strong><br />
I want to build a small Raspberry Pi cluster to learn Kubernetes in a tangible, experimental way. It’s a chance to explore container orchestration beyond theory and documentation.</p>
  </li>
  <li>
    <p><strong>AI-Powered Etsy Listing Generator</strong><br />
Jewelry making is one of my hobbies, but my Etsy shop often sits neglected. Supplies are costly, and I’d love to improve my selling process. My idea: an AI tool that takes a photo of a piece, generates an SEO-optimized listing, and posts it directly to Etsy via their API.</p>

    <p>I’ll treat this project like a client engagement—implementing automation with CI/CD pipelines using GitHub Actions, infrastructure as code with Terraform, and leveraging AI coding assistance. In short, I want to walk through the entire software lifecycle, but for a project that’s personal and fun.</p>
  </li>
</ul>

<h2 id="why-this-blog-matters">Why This Blog Matters</h2>

<p>I hope this blog inspires others to rediscover the joy of building. We became software engineers because we love to code, but somewhere along the way, deadlines, meetings, and workplace drama can make us forget that spark.</p>

<p>By sharing my journey—the frustrations, the experiments, the wins—I want to show that upskilling doesn’t have to feel like work. It can be a creative outlet, a way to reconnect with why we started coding in the first place.</p>

<p>So here’s to learning, tinkering, and building projects that matter to us.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[For the past three years, my career has revolved around technical project leadership. I’ve been in meetings, guiding engineers, and making architectural recommendations based largely on documentation. It’s the role many architects find themselves in—strategic, high-level, but increasingly disconnected from the hands-on work that originally drew us into technology.]]></summary></entry></feed>