<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ivan SZKIBA</title>
    <description>The latest articles on DEV Community by Ivan SZKIBA (@szkiba).</description>
    <link>https://dev.to/szkiba</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192487%2Fb9529d5e-c896-4c72-b655-664b67452fb5.png</url>
      <title>DEV Community: Ivan SZKIBA</title>
      <link>https://dev.to/szkiba</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/szkiba"/>
    <language>en</language>
    <item>
      <title>Dev Containers: The Missing Runtime for Agentic Teams</title>
      <dc:creator>Ivan SZKIBA</dc:creator>
      <pubDate>Sat, 04 Apr 2026 13:48:15 +0000</pubDate>
      <link>https://dev.to/szkiba/dev-containers-the-missing-runtime-for-agentic-teams-27md</link>
      <guid>https://dev.to/szkiba/dev-containers-the-missing-runtime-for-agentic-teams-27md</guid>
      <description>&lt;p&gt;The agentic era of software engineering is already here. We have moved beyond autocomplete into a world where tools like &lt;strong&gt;Claude Code&lt;/strong&gt; and &lt;strong&gt;Cursor&lt;/strong&gt; can inspect codebases, run tests, refactor modules, and propose meaningful changes with very little supervision.&lt;/p&gt;

&lt;p&gt;But there is still a quiet productivity killer in many AI-assisted workflows: &lt;strong&gt;environmental friction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you have ever watched an agent fail because a compiler is missing, a runtime version is wrong, or a dependency exists on one machine but not another, then you have seen the real problem. In professional teams, the answer is not just better prompts. It is better infrastructure.&lt;/p&gt;

&lt;p&gt;Imagine an agent opens your repo, sees &lt;code&gt;pom.xml&lt;/code&gt;, confidently writes Java 21 code, and then fails because the actual machine still has Java 17 installed. That is not an AI problem. That is an environment problem.&lt;/p&gt;

&lt;p&gt;Most teams are trying to solve this with prompts, conventions, and Markdown files. Those things matter, but they are not enough. The missing layer in agentic software engineering is a &lt;strong&gt;shared, versioned runtime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If AI agents are becoming contributors to the codebase, they need the same thing every good teammate needs on day one: a working development environment.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Dev Container Advantage: A Shared Runtime
&lt;/h2&gt;

&lt;p&gt;When a new contributor joins a project, the goal is simple: get them productive quickly and predictably.&lt;/p&gt;

&lt;p&gt;A Dev Container gives both humans and agents the same starting point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same operating system base image&lt;/li&gt;
&lt;li&gt;The same toolchain versions&lt;/li&gt;
&lt;li&gt;The same project dependencies&lt;/li&gt;
&lt;li&gt;The same terminal commands and failure modes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That matters because "ready to work" should mean the same thing for every contributor touching the repository, whether they are typing or delegating to an agent. In practice, the onboarding target is no longer just a developer. It is a developer-agent pair working against the same codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Markdown Isn't an Environment
&lt;/h2&gt;

&lt;p&gt;Files like &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;SKILLS.md&lt;/code&gt;, and &lt;code&gt;.cursorrules&lt;/code&gt; are useful. They capture conventions, workflows, and expectations. But they do not create a reproducible runtime on their own.&lt;/p&gt;

&lt;p&gt;They are governance artifacts, not infrastructure. They can tell a contributor what should happen, but they cannot guarantee that the required tools, runtimes, or permissions actually exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. For Humans: Manual setup does not scale
&lt;/h3&gt;

&lt;p&gt;Asking every developer to manually recreate the project environment is expensive and error-prone.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The DX tax:&lt;/strong&gt; Every new teammate burns time on &lt;code&gt;brew install&lt;/code&gt;, &lt;code&gt;apt install&lt;/code&gt;, language managers, and local machine quirks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The drift problem:&lt;/strong&gt; One missed package, one wrong version, or one skipped step is enough to create a broken setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. For Agents: Instructions are not guarantees
&lt;/h3&gt;

&lt;p&gt;Telling an agent "Use Java 21" only helps if Java 21 is actually available in the execution environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Re-verification waste:&lt;/strong&gt; Agents spend time and tokens checking whether the machine matches the written instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host pollution risk:&lt;/strong&gt; If the fallback plan is "install whatever is missing on the host," your workstation accumulates conflicting global installs that are hard to untangle later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Markdown tells contributors &lt;strong&gt;how&lt;/strong&gt; to work. A Dev Container defines &lt;strong&gt;where&lt;/strong&gt; that work happens. Strong teams usually need both, but only one of them actually constrains the execution environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Dev Containers Work for Team-Owned Projects
&lt;/h2&gt;

&lt;p&gt;For a shared codebase, the environment should be version-controlled alongside the application itself. A &lt;code&gt;.devcontainer/devcontainer.json&lt;/code&gt; lets the team treat the runtime as part of the project, not tribal knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Less version drift
&lt;/h3&gt;

&lt;p&gt;In a polyglot project using &lt;strong&gt;Java, Go, Rust, and Python&lt;/strong&gt;, consistency is hard to maintain by hand.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The risk:&lt;/strong&gt; An agent writes code using Java 21 features while CI is still running Java 17.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The improvement:&lt;/strong&gt; You update the container config once, rebuild or reopen the workspace, and the whole team moves toward the same baseline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Shared debugging context
&lt;/h3&gt;

&lt;p&gt;Supervising an agent is much easier when you are not dealing with a hidden machine state.&lt;/p&gt;

&lt;p&gt;If you and the agent work inside the same container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You see the same files and the same toolchain.&lt;/li&gt;
&lt;li&gt;You hit the same compiler and test errors.&lt;/li&gt;
&lt;li&gt;You can step into the exact same shell session pattern and rerun the same command the agent just executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That removes the classic "works on my machine" translation layer before it starts.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Minimal Polyglot Setup
&lt;/h2&gt;

&lt;p&gt;You do not always need a custom Dockerfile. For many teams, a single &lt;code&gt;devcontainer.json&lt;/code&gt; with a base image plus features is enough to create a strong shared workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example &lt;code&gt;.devcontainer/devcontainer.json&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Polyglot Team Workspace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcr.microsoft.com/devcontainers/base:ubuntu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io/devcontainers/features/java:1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"21"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io/devcontainers/features/go:1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.24"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io/devcontainers/features/rust:1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io/devcontainers/features/python:1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.13"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io/devcontainers/features/github-cli:1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customizations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vscode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"vscjava.vscode-java-pack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"golang.go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"rust-lang.rust-analyzer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ms-python.python"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"remoteUser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vscode"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact versions should match what your project and CI actually support. The important part is not these specific numbers. It is that the environment definition lives in the repository and can evolve with the code.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Teams Actually Use It
&lt;/h2&gt;

&lt;p&gt;One of the best parts of the Dev Container standard is that it does not force a single editor or workflow. Different tools can still share the same environment definition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow A: IDE-first
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The setup:&lt;/strong&gt; Open the project in a supporting editor using the repository's dev container configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For humans:&lt;/strong&gt; You get language servers, debugging, extensions, and terminal access without installing every SDK on the host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For agent-assisted editors:&lt;/strong&gt; The exact UX varies by tool, but the editor and its built-in agent features can operate against the containerized workspace instead of a drifting host setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Workflow B: Terminal-first
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The setup:&lt;/strong&gt; Use a tool like &lt;strong&gt;DevPod&lt;/strong&gt; or the Dev Container CLI to create the workspace and attach a shell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For humans:&lt;/strong&gt; You can stay in &lt;strong&gt;Neovim&lt;/strong&gt;, &lt;strong&gt;tmux&lt;/strong&gt;, or your preferred shell workflow while still using the shared toolchain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For CLI agents:&lt;/strong&gt; Agents launched from inside that container inherit the same runtimes, compilers, and project dependencies available to the human operator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is not that every tool behaves identically. The result is that they start from the same runtime assumptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security: Reduce the Blast Radius
&lt;/h2&gt;

&lt;p&gt;A Dev Container is not magic security. It does not automatically make an agent harmless, and it should not be treated as a complete security boundary.&lt;/p&gt;

&lt;p&gt;What it &lt;em&gt;does&lt;/em&gt; give you is a cleaner way to reduce exposure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep project tooling inside the container instead of on the host&lt;/li&gt;
&lt;li&gt;Mount only what the task actually needs&lt;/li&gt;
&lt;li&gt;Avoid exposing personal SSH keys, cloud credentials, or global config by default&lt;/li&gt;
&lt;li&gt;Separate experimental agent workflows from your everyday workstation setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a practical security improvement. Not perfect isolation, but a smaller blast radius and a more deliberate operating model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Build for the Team, Not Just the Prompt
&lt;/h2&gt;

&lt;p&gt;Software teams now include both people and agents as active contributors. If we want that model to work reliably, we need environments that belong to the project rather than to whoever happens to run the task.&lt;/p&gt;

&lt;p&gt;The industry is quickly learning how to write better prompts and better agent instructions. The next lesson is that guidance without runtime control is incomplete infrastructure.&lt;/p&gt;

&lt;p&gt;When the environment is versioned, reproducible, and shared, both people and agents can contribute with less setup friction, less guesswork, and less machine-specific chaos.&lt;/p&gt;

&lt;p&gt;Treat the runtime as part of the product. In the agentic era, the repository is not enough. The team has to version the environment too.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Are you using Dev Containers in your AI workflows today? What has worked well, and where is the friction still showing up?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>agents</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Git-Native Agent Orchestration: A Stateless Maker-Checker Pattern</title>
      <dc:creator>Ivan SZKIBA</dc:creator>
      <pubDate>Mon, 23 Mar 2026 18:05:03 +0000</pubDate>
      <link>https://dev.to/szkiba/git-native-agent-orchestration-a-stateless-maker-checker-pattern-4956</link>
      <guid>https://dev.to/szkiba/git-native-agent-orchestration-a-stateless-maker-checker-pattern-4956</guid>
      <description>&lt;p&gt;This guide introduces a drop-in architectural pattern for teams building multi-agent AI systems. It utilizes native Git features to orchestrate a stateless, collision-free Maker-Checker loop without relying on complex Python frameworks, external databases, or heavy memory management.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Current Agent Frameworks
&lt;/h2&gt;

&lt;p&gt;When building autonomous Maker-Checker loops (e.g., a Developer Agent writing code and a Reviewer Agent testing it), engineers often encounter three major friction points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;State Management:&lt;/strong&gt; Passing large chunks of code, architectural plans, or evaluation feedback back and forth via JSON payloads or database entries is bulky and prone to token-limit failures.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Workspace Pollution:&lt;/strong&gt; Agents operating in the same directory overwrite each other's files, trigger race conditions, or cause file-lock crashes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;PR Noise:&lt;/strong&gt; Trial-and-error AI commits clutter the remote repository, making human code review a nightmare.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Git-Native Solution
&lt;/h2&gt;

&lt;p&gt;This workflow entirely eliminates those issues by treating the Git database itself as the state machine. The Orchestrator is reduced to a "dumb" trigger, while the agents manage the state autonomously using the following mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git Worktrees:&lt;/strong&gt; Agents are physically isolated in ephemeral sibling directories (&lt;code&gt;foo-wt/maker&lt;/code&gt; and &lt;code&gt;foo-wt/checker&lt;/code&gt;), completely preventing file collisions while sharing the same underlying &lt;code&gt;.git&lt;/code&gt; repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-Aware Agents:&lt;/strong&gt; Agents are not hardcoded with static prompts. Instead, their first action upon waking up is to execute &lt;code&gt;git branch --show-current&lt;/code&gt;. They dynamically parse their branch name scheme to determine their role, phase, and task context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-Driven Handoffs via Git Notes:&lt;/strong&gt; Instead of communicating via an API, the agents leave prompts for each other directly on the commits. The Maker creates a commit and uses &lt;code&gt;git notes add&lt;/code&gt; to request a review. The Checker evaluates the code and uses &lt;code&gt;git notes append&lt;/code&gt; to attach its Markdown feedback. Neither agent ever overwrites history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-Generated Transcripts &amp;amp; Memory:&lt;/strong&gt; Before squash merging, the Orchestrator dumps the complete &lt;code&gt;git notes&lt;/code&gt; history into a Markdown transcript (&lt;code&gt;history-XXX.md&lt;/code&gt;) and saves it to an isolated root branch (&lt;code&gt;devlog&lt;/code&gt;). This auto-generates a perfect "AI thought process" engineering blog draft without polluting the production codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Squash Merging:&lt;/strong&gt; Only the final, Checker-approved iteration is squash-merged into the integration branch, resulting in a pristine Pull Request for human reviewers.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Want the raw files?&lt;/strong&gt; If you just want to drop this into your own project, you can &lt;a href="https://gist.github.com/szkiba/740aaff981dba3ce11265d8e4c1832d3" rel="noopener noreferrer"&gt;download the original ORCHESTRATION.md from my GitHub Gist&lt;/a&gt;. For the full architectural deep dive, read on.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Architecture: Multi-Agent Orchestration Workflow (Maker-Checker)
&lt;/h2&gt;

&lt;p&gt;This outlines the workflow architecture for the multi-agent system. The core engine of this system relies entirely on a Maker-Checker pattern to iteratively generate, evaluate, and refine artifacts—ranging from software code to design specifications and whitepapers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note on Terminology: In this document, &lt;code&gt;foo&lt;/code&gt; represents your actual repository name. &lt;code&gt;XXX&lt;/code&gt; represents a URL-friendly task identifier, exactly like the branch names GitHub automatically generates from issue titles—e.g., &lt;code&gt;123-add-auth&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Core Principles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;General Purpose:&lt;/strong&gt; This workflow applies to any text-based artifact (Code, Markdown, JSON, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical Isolation:&lt;/strong&gt; Agents operate in strictly isolated file systems using Git Worktrees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous State Machine:&lt;/strong&gt; The Orchestrator is a "dumb" trigger. The agents themselves drive the state machine by passing prompts and feedback to each other exclusively through &lt;code&gt;git notes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Golden Rule of Notes:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Maker&lt;/strong&gt; creates new commits and exclusively uses &lt;code&gt;git notes add&lt;/code&gt; to attach review requests. &lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Checker&lt;/strong&gt; never creates commits; it exclusively uses &lt;code&gt;git notes append&lt;/code&gt; to add feedback to the Maker's commit. Neither agent ever overwrites history.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Read-Only Checker:&lt;/strong&gt; The Checker agent strictly evaluates work. It may execute tests and append &lt;code&gt;git notes&lt;/code&gt;, but it must &lt;em&gt;never&lt;/em&gt; modify project files or create standard commits.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Context-Aware Agents:&lt;/strong&gt; Agents determine their exact role, phase, and task dynamically upon initialization by executing &lt;code&gt;git branch --show-current&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Agnostic Orchestrator:&lt;/strong&gt; The "Orchestrator" does not need to be a complex AI framework. It can be a simple Bash script, a CI/CD pipeline, or even a human developer manually typing Git commands in the terminal. Its only job is to trigger the agents and manage the final merge.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Infrastructure &amp;amp; Environment
&lt;/h3&gt;

&lt;p&gt;To prevent file-lock collisions and environment drift, the repository is split into the Orchestrator context and active Worktree sandboxes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Directory Structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace/
├── foo-wt/                  # Ephemeral worktree sandboxes
│   ├── feature/             # The integration baseline
│   ├── maker/               # Maker's workspace
│   └── checker/             # Checker's workspace
└── foo/                     # Main Orchestrator repository
    ├── .git/                # Shared Git repository
    └── ORCHESTRATION.md     # This file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Branching &amp;amp; Push Strategy
&lt;/h3&gt;

&lt;p&gt;For every new task or document generation (&lt;code&gt;XXX&lt;/code&gt;), the Orchestrator manages a strict branching lifecycle. &lt;strong&gt;Strict remote isolation is enforced to prevent repository pollution.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Worktree&lt;/th&gt;
&lt;th&gt;Remote Policy&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feature/XXX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;foo-wt/feature/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pushed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The integration branch. Pushed to remote for final PR review.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;devlog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;N/A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pushed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated root branch. Acts as the permanent knowledge base for AI plans.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maker/XXX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;foo-wt/maker/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Never Pushed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Branched from &lt;code&gt;feature&lt;/code&gt; or &lt;code&gt;devlog&lt;/code&gt;. Contains the Maker's raw, messy iterative commits.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;checker/XXX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;foo-wt/checker/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Never Pushed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Branched from &lt;code&gt;feature&lt;/code&gt; or &lt;code&gt;devlog&lt;/code&gt;. The Checker pulls the Maker's commits here to run evaluations safely.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnuoi3960n8iibcopbrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnuoi3960n8iibcopbrg.png" alt="Branching strategy" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The Workflow Execution Loop
&lt;/h3&gt;

&lt;p&gt;The Maker and Checker never communicate directly via API or memory. They pass code forward via Git commits and pass execution prompts backward and forward via &lt;code&gt;git notes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To prevent agent hallucination and ensure strict context, every prompt written to a &lt;code&gt;git note&lt;/code&gt; must explicitly reference the exact commit hash it applies to (e.g., &lt;code&gt;[REVIEW] Evaluate commit 7a8b9c0&lt;/code&gt;).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Step-by-Step Handoff Protocol&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Anchor (Orchestrator):&lt;/strong&gt; The Orchestrator creates an empty commit on &lt;code&gt;maker/XXX&lt;/code&gt; and adds a &lt;code&gt;git note&lt;/code&gt; containing the initial, completely stateless task prompt. &lt;em&gt;(Example: &lt;code&gt;git notes add -m "[TASK] Add a /health endpoint to server.js returning 200 OK"&lt;/code&gt;). Notice there is no complex persona or framework context—just the raw task.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maker Iteration:&lt;/strong&gt; The Maker reads the note on &lt;code&gt;HEAD&lt;/code&gt;, writes code, and creates &lt;strong&gt;Commit A&lt;/strong&gt;. The Maker then runs &lt;code&gt;git notes add&lt;/code&gt; on Commit A, tagging it with a &lt;code&gt;[REVIEW]&lt;/code&gt; prompt asking the Checker to evaluate it (e.g., &lt;code&gt;[REVIEW] Evaluate commit 7a8b9c0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checker Iteration:&lt;/strong&gt; The Checker reads the note on Commit A, evaluates the code, and runs &lt;code&gt;git notes append&lt;/code&gt; on Commit A with its feedback, tagging it with a &lt;code&gt;[REVISE]&lt;/code&gt; prompt for the Maker to fix the identified issues (e.g., &lt;code&gt;[REVISE] Fix null pointer in commit 7a8b9c0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Loop Continues:&lt;/strong&gt; The Maker reads the updated note on Commit A, fixes the code, creates &lt;strong&gt;Commit B&lt;/strong&gt;, and executes a new &lt;code&gt;git notes add&lt;/code&gt; review request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; Once the Checker's evaluation passes, it appends an &lt;code&gt;[APPROVED]&lt;/code&gt; note. The Orchestrator sees this, generates the transcript, and squash merges the branch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Max Iterations Guardrail&lt;/strong&gt;&lt;br&gt;
To prevent infinite loops, the Orchestrator enforces a strict limit (e.g., 5 iterations). If the Maker fails to achieve Checker approval within this limit, the loop aborts and escalates to a human.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwatrie4pkehi48myeqxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwatrie4pkehi48myeqxg.png" alt="Workflow Loop" width="800" height="823"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Merging &amp;amp; Feedback Storage Rules
&lt;/h3&gt;

&lt;p&gt;To maintain a pristine project history while simultaneously preserving the AI's "thought process" for documentation, strict rules are enforced.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 1: Transcript Generation (For Engineering Blogs)&lt;/strong&gt;&lt;br&gt;
Before the Orchestrator merges the Maker's code into production, it compiles the complete AI conversation history. To prevent polluting the production codebase, this transcript is always saved directly to the isolated &lt;code&gt;devlog&lt;/code&gt; knowledge-base branch. &lt;/p&gt;

&lt;p&gt;Because every prompt, Maker commit, and Checker critique is stored in the Git notes, the Orchestrator simply dumps this into a Markdown transcript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Generate the transcript from the Maker's branch&lt;/span&gt;
git log maker/XXX &lt;span class="nt"&gt;--show-notes&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"### Commit: %h%n**Maker:** %B%n%N%n---"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; history-XXX.md

&lt;span class="c"&gt;# 2. Save it exclusively to the devlog branch&lt;/span&gt;
git checkout devlog
&lt;span class="nb"&gt;mv &lt;/span&gt;history-XXX.md transcripts/
git add transcripts/history-XXX.md
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"docs: save iteration transcript for task XXX"&lt;/span&gt;

&lt;span class="c"&gt;# 3. Return to the target branch to merge the actual code&lt;/span&gt;
git checkout feature/XXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule 2: Squash Merging Only&lt;/strong&gt;&lt;br&gt;
Once the transcript is safely committed to &lt;code&gt;devlog&lt;/code&gt;, the Orchestrator &lt;strong&gt;must squash merge&lt;/strong&gt; the actual code from &lt;code&gt;maker/XXX&lt;/code&gt; into the target integration branch (typically &lt;code&gt;feature/XXX&lt;/code&gt;) using &lt;code&gt;git merge --squash maker/XXX&lt;/code&gt;. This condenses dozens of broken, AI-generated trial-and-error commits into a single, clean commit for human reviewers.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Cleanup Guardrails
&lt;/h3&gt;

&lt;p&gt;Upon successful PR creation or plan approval, the Orchestrator enforces strict teardown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;(If applicable) Destroy the Maker and Checker containerized environments.&lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;git worktree remove&lt;/code&gt; for both sandbox directories.&lt;/li&gt;
&lt;li&gt;Delete local &lt;code&gt;maker/XXX&lt;/code&gt; and &lt;code&gt;checker/XXX&lt;/code&gt; branches.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  7. Agent System Instructions
&lt;/h3&gt;

&lt;p&gt;Because the system runs entirely through Git state, the Orchestrator does not generate complex prompts. Instead, the AI agents are configured with the following baseline system instructions regarding how they interact with the Git database:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maker Agent System Prompt&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"When triggered, run &lt;code&gt;git notes show HEAD&lt;/code&gt; to read your current instructions. If it is a new task, write the code. If it is Checker feedback, fix the code. Once your work is done, you must create a standard git commit. Then, you must run &lt;code&gt;git notes add -m '[REVIEW] &amp;lt;your review request&amp;gt;' HEAD&lt;/code&gt; to attach a prompt for the Checker to evaluate your new commit."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Checker Agent System Prompt&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"When triggered, run &lt;code&gt;git merge maker/XXX&lt;/code&gt; to pull the Maker's latest commit into your workspace. Run &lt;code&gt;git notes show HEAD&lt;/code&gt; to read the Maker's review request. Run tests and evaluate the code. You must never create a new commit. Instead, you must run &lt;code&gt;git notes append -m '[REVISE] &amp;lt;your feedback&amp;gt;' HEAD&lt;/code&gt; to attach your feedback to the Maker's commit. If the code is perfect, append &lt;code&gt;[APPROVED]&lt;/code&gt;."&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  8. Advanced Pattern: Plan &amp;amp; Execute (Optional)
&lt;/h3&gt;

&lt;p&gt;For complex tasks, large language models perform significantly better when planning is separated from execution. The Orchestrator can utilize a two-phase "Plan &amp;amp; Execute" pattern using an isolated knowledge-base branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;devlog&lt;/code&gt; Knowledge Base&lt;/strong&gt;&lt;br&gt;
Instead of storing AI plans in the &lt;code&gt;main&lt;/code&gt; branch, a dedicated isolated root branch named &lt;code&gt;devlog&lt;/code&gt; is maintained. This acts as the long-term memory for the AI system. Plans are generated here by the Maker and Checker, and the resulting &lt;code&gt;transcripts/history-XXX.md&lt;/code&gt; acts as a perfectly formatted draft for an engineering blog post detailing how the system designed the feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standardizing Plans with Frontmatter&lt;/strong&gt;&lt;br&gt;
To ensure plans are machine-readable, the Maker agent is instructed to include standard YAML frontmatter at the top of every &lt;code&gt;plan-XXX.md&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example &lt;code&gt;plan-XXX.md&lt;/code&gt; format:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Implement&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;OAuth2&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Authentication"&lt;/span&gt;
&lt;span class="na"&gt;task_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;123-add-oauth"&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-03-16&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Phase 1: The Planning Phase&lt;/strong&gt;&lt;br&gt;
The Orchestrator initiates the Maker-Checker loop targeting the &lt;code&gt;devlog&lt;/code&gt; branch. The Orchestrator's anchor commit note tells the Maker: &lt;em&gt;"Draft the architecture plan for task XXX."&lt;/em&gt; The loop runs until the Checker appends &lt;code&gt;[APPROVED]&lt;/code&gt;. The Orchestrator generates the transcript and squash merges into &lt;code&gt;devlog&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: The Execution Phase&lt;/strong&gt;&lt;br&gt;
The Orchestrator initiates a second Maker-Checker loop, this time targeting the &lt;code&gt;feature/XXX&lt;/code&gt; branch. The Orchestrator reads the approved &lt;code&gt;plans/plan-XXX.md&lt;/code&gt; from &lt;code&gt;devlog&lt;/code&gt; and attaches it to the empty anchor commit as the Maker's instructions. The loop runs until &lt;code&gt;[APPROVED]&lt;/code&gt;, ending in a clean feature PR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvo39i4y8h9kjv5dyldg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvo39i4y8h9kjv5dyldg.png" alt="Advanced Workflow" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Origin Story: Grafana Hackathon 16
&lt;/h2&gt;

&lt;p&gt;This Git-native architecture wasn't born in a vacuum—it evolved from real developer friction. &lt;/p&gt;

&lt;p&gt;During &lt;strong&gt;Grafana Hackathon 16&lt;/strong&gt;, I was heavily experimenting with autonomous AI agent coding. I quickly ran into the pain points of managing state and passing files between multiple agents. To fix this on the fly, I intuitively built a two-agent feedback loop. It wasn't until later, while reading up on AI architecture, that I realized I had naturally arrived at the industry-standard &lt;strong&gt;Maker-Checker pattern&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;However, while the pattern worked, the underlying plumbing was still messy. &lt;em&gt;After&lt;/em&gt; the hackathon, reflecting on the friction of file-locks and state management, the "Git-Native" concept finally clicked. I realized we didn't need to build complex ways to pass files between agents—Git was already the perfect, battle-tested state machine for the job.&lt;/p&gt;

&lt;p&gt;A massive thank you to Grafana for consistently providing access to the latest AI technologies and models. Furthermore, the hackathon itself provided the perfect dedicated space to experiment with new ideas. That freedom to just "build and break things" is exactly what made this architecture possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next: Executable Skills &amp;amp; Battle Testing
&lt;/h2&gt;

&lt;p&gt;This orchestration document is just the foundation. Over the coming weeks, I am taking this theoretical architecture and battle-testing it inside a live, production-scale project. &lt;/p&gt;

&lt;p&gt;Once the pattern is fully refined under real-world conditions, I will be publishing &lt;strong&gt;Git-Native Agent Skills&lt;/strong&gt; (utilizing the new Anthropic &lt;code&gt;SKILL.md&lt;/code&gt; format) in a dedicated, full GitHub repository. &lt;/p&gt;

&lt;p&gt;These drop-in skills will allow CLI agents like Claude Code to natively understand and execute this exact Maker-Checker Git loop with zero extra framework configuration. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;(This article was originally published on my blog at &lt;a href="https://codecommis.hashnode.dev/git-native-maker-checker" rel="noopener noreferrer"&gt;codecommis.hashnode.dev&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/szkiba" rel="noopener noreferrer"&gt;Follow me on GitHub&lt;/a&gt; to get notified when the official repository and executable skills drop!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>git</category>
      <category>makerchecker</category>
    </item>
    <item>
      <title>Testable Example Code</title>
      <dc:creator>Ivan SZKIBA</dc:creator>
      <pubDate>Tue, 09 Jan 2024 18:10:22 +0000</pubDate>
      <link>https://dev.to/szkiba/testable-example-code-41on</link>
      <guid>https://dev.to/szkiba/testable-example-code-41on</guid>
      <description>&lt;p&gt;&lt;em&gt;There is no worse developer experience than bad example code in the documentation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It is easy to make mistakes in code slightly more complicated than a &lt;em&gt;Hello World&lt;/em&gt; program. In fact, in &lt;em&gt;Hello World&lt;/em&gt; as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can you avoid it?
&lt;/h3&gt;

&lt;p&gt;It is advisable to test the example codes in the documentation, like all code. Since the example codes also change over time, this testing should be done automatically, built into the documentation release process.&lt;/p&gt;

&lt;p&gt;Basically, we can choose from two options if we want to test the example codes embedded in the documentation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the example code in a separate file and embed this file, or part of it, in the documentation&lt;/li&gt;
&lt;li&gt;The example code included in the documentation is extracted into a file, if necessary, supplemented to make it a valid source file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In both cases, finally, testing the example code is a normal testing task. So the example code can be tested using the standard test framework in the given programming environment. This testing step can be integrated into the build process, guaranteeing that the example codes work correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Required tooling
&lt;/h3&gt;

&lt;p&gt;For the above, we need a tool that can embed source files or a part of them in the documentation or can create valid source files from the example codes in the documentation (supplementing them if necessary).&lt;/p&gt;

&lt;p&gt;Today, the most popular format for developer documentation is the Markdown format. Example code pieces can be placed in a Markdown document using so-called fenced code blocks.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/szkiba/mdcode"&gt;mdcode&lt;/a&gt; command-line tool was created for the development and testing of Markdown fenced code blocks. It supports both example code testing/development processes: it is able to keep Markdown code blocks in sync with external source files, or part of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Show me the code!
&lt;/h3&gt;

&lt;p&gt;Using &lt;a href="https://github.com/szkiba/mdcode"&gt;mdcode&lt;/a&gt; is super easy. The first line of the fenced code block, the so-called &lt;em&gt;info-string&lt;/em&gt;, must be supplemented with some metadata after the programming language. Such metadata is the &lt;code&gt;file&lt;/code&gt;, which specifies the name of the file belonging to the code block. The &lt;code&gt;mdcode extract&lt;/code&gt; command writes the contents of the code block into this file, and the &lt;code&gt;mdcode update&lt;/code&gt; command updates the contents of the code block from here.&lt;/p&gt;

&lt;p&gt;Sticking to the &lt;em&gt;Hello World&lt;/em&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, Testable World!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how to add the file metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```js file=hello.js
console.log("Hello, Testable World!")
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The content of this code block can be saved to file (&lt;code&gt;hello.js&lt;/code&gt;) using the &lt;code&gt;mdcode extract&lt;/code&gt; command. Then the &lt;em&gt;Hello World&lt;/em&gt; example can be tested with the following test code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, Testable World!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./hello.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test code can be embedded without being visible in the markdown document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!--&amp;lt;script type="text/markdown"&amp;gt;
```js file=hello.test.js
const assert = require("node:assert")
const test = require("node:test")

test("hello", (t) =&amp;gt; {
    console.log = function (message) {
        assert.equal(message, "Hello, Testable World!")
    }
    require("./hello.js")
})
```
&amp;lt;/script&amp;gt;--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is advisable to include the test code in the document as well, because in this way the example codes can be tested by anyone with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdcode extract testable-markdown-code-blocks.md
node &lt;span class="nt"&gt;--test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;testable-markdown-code-blocks.md&lt;/code&gt; is the name of the document. Of course, any test framework can be used for testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  A more realistic example
&lt;/h3&gt;

&lt;p&gt;A slightly more realistic case when the example code is only a fragment, for example a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many editors (such as Visual Studio Code) support the use of so-called &lt;code&gt;#region&lt;/code&gt; comments for code folding. Regions marked in this way are usually named for readability. To avoid having to learn new, useless syntax, &lt;a href="https://github.com/szkiba/mdcode"&gt;mdcode&lt;/a&gt; uses &lt;code&gt;#region&lt;/code&gt; comments to mark embeddable parts of the source code. Simply add a &lt;code&gt;region&lt;/code&gt; metadata to the fenced code block which contains the name of the region you want to embed.&lt;/p&gt;

&lt;p&gt;This is how the code block above can be embedded from the region named &lt;code&gt;function&lt;/code&gt; in the &lt;code&gt;factorial.test.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```js file=factorial.test.js region=function
function factorial(n) {
    if (n &amp;gt; 1) {
        return n * factorial(n - 1)
    }

    return 1
}
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To do this, in the &lt;code&gt;factorial.test.js&lt;/code&gt; file, you simply mark the part of the code you want to embed with a &lt;code&gt;#region&lt;/code&gt; comment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// #region function&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// #endregion&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testvect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5040&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;362880&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3628800&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;factorial with test vector&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;testvect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;testvect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to make the document self-contained, you can embed the test code in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!--&amp;lt;script type="text/markdown"&amp;gt;
```js file=factorial.test.js outline=true
const assert = require("node:assert")
const test = require("node:test")

// #region function
// #endregion

const testvect = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

test("factorial with test vector", (t) =&amp;gt; {
    for (var i = 0; i &amp;lt; testvect.length; i++) {
        assert.equal(factorial(i), testvect[i])
    }
})
```
&amp;lt;/script&amp;gt;--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to place this invisible code block before the visible code block in the document, because &lt;code&gt;mdcode extract&lt;/code&gt; processes the code blocks in order.&lt;/p&gt;

&lt;p&gt;It is worth noting that this code block only contains the outline of the file, so the  &lt;code&gt;#region&lt;/code&gt; part is empty. &lt;code&gt;mdcode extract&lt;/code&gt; will automatically fill in the part between the &lt;code&gt;#region&lt;/code&gt; comment, and &lt;code&gt;mdcode update&lt;/code&gt; will automatically delete it during embedding. Such outline code blocks are marked by the &lt;code&gt;outline&lt;/code&gt; metadata value &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I edit the examples?
&lt;/h3&gt;

&lt;p&gt;The example codes can also be edited within the markdown document. Most IDEs also provide syntax highlighting support within markdown fenced code blocks. However, we don't get actual programming language support (code formatting, IntelliSense, etc.)&lt;/p&gt;

&lt;p&gt;It is more convenient to develop and test the example codes in actual source files. The parts you want to embed simply have to be marked with &lt;code&gt;#region&lt;/code&gt; comments and can be updated in the markdown document using the &lt;code&gt;mdcode update&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The examples in this document and the corresponding tests can be extracted into files with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdcode extract testable-markdown-code-blocks.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, the following files are created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;factorial.test.js
hello.js
hello.test.js 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These can be modified and then tested with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, the document can be updated using the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdcode update testable-markdown-code-blocks.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;You can find the original post here: &lt;a href="https://gist.github.com/szkiba/d9b7b4601efb7dc68aa2c58548b8b271"&gt;https://gist.github.com/szkiba/d9b7b4601efb7dc68aa2c58548b8b271&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@helloimnik?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Nik&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/brown-book-page-Im93gzsDbf4?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>testing</category>
      <category>documentation</category>
      <category>cli</category>
    </item>
    <item>
      <title>PhantAuth: Random User Generator + OpenID Connect Provider</title>
      <dc:creator>Ivan SZKIBA</dc:creator>
      <pubDate>Tue, 09 Jul 2019 11:49:48 +0000</pubDate>
      <link>https://dev.to/szkiba/phantauth-random-user-generator-openid-connect-provider-10l9</link>
      <guid>https://dev.to/szkiba/phantauth-random-user-generator-openid-connect-provider-10l9</guid>
      <description>&lt;p&gt;Let me introduce PhantAuth: Random User Generator + OpenID Connect Provider. Like Lorem Ipsum, but for user accounts and authentication. PhantAuth was designed to simplify testing for applications using OpenID Connect authentication by making use of random generated users. Check PhantAuth Developer Portal for more information: &lt;a href="https://www.phantauth.net"&gt;https://www.phantauth.net&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
