<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Devon Burriss' Blog</title>
  <link href="https://devonburriss.me/" />
  <link type="application/atom+xml" rel="self" href="https://devonburriss.me/atom.xml" />
  <updated>2026-01-26T05:35:38Z</updated>
  <id>https://devonburriss.me/</id>
  <author>
    <name>Devon Burriss</name>
    <email></email>
  </author>
  <entry>
    <id>https://devonburriss.me/llms-not-junior/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/llms-not-junior/" />
    <title>LLMs are not junior programmers</title>
    <updated>2025-12-21T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/llms-not-junior/</uri>
    </author>
    <summary type="html">&lt;p&gt;I often hear people compare LLM tools to junior developers. This comparison is not only incorrect, it is holding you back. If you have an incorrect mental model of the tool, you will constantly be surprised by the results. In this post I will explain how viewing the LLM as a junior is holding you back from better outcomes.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;It should be no surprise that a large language model (LLM) is not like a human. The marketing hype has been pushing the Artificial Intelligence narrative for years now though. As social primates, language is integral for how we communicate with other humans, so it is only natural that we anthropomorphise these machines that are so adept at language.&lt;/p&gt;
&lt;p&gt;It is important to know what you are dealing with. I summarise it as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The LLM is a stateless, stochastic function: &lt;code&gt;(parameters, prompt, runtime config) → output&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So what does this mean?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stateless: It does not remember. The output is based on the input.&lt;/li&gt;
&lt;li&gt;Stochastic: The next generated token is based on a probability distribution of what has come before in the text. Effectively, it is non-deterministic.&lt;a id="fnref:1" href="#fn:1" class="footnote-ref"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Function: It generates the next token based on what has come before. The output is the result of a sequence of next most likely tokens.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hopefully, this is a useful grounding as we go through the precise ways that a coding assistant is not like a junior programmer.&lt;/p&gt;
&lt;h2 id="a-junior-learns"&gt;1. A junior learns&lt;/h2&gt;
&lt;p&gt;A junior is responsible for learning from their experience. As they work and are mentored, they gain knowledge and skills that they bring into their next task. They improve.&lt;/p&gt;
&lt;p&gt;The agentic tools like Claude, OpenCode, or Copilot do not learn. They are stateless. All context that will determine a high or low quality output needs to be supplied by you or your tooling.&lt;/p&gt;
&lt;p&gt;Knowing this as the operator of the agentic coding tool, it is your responsibility to &amp;ldquo;teach&amp;rdquo; the agent as you go. The trick is that you need to teach it with every input. I have heard this phrased as &amp;ldquo;A highly skilled programmer with amnesia&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;There are many techniques for giving these agents memory but the two easiest and most commonly used are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://agents.md/"&gt;AGENTS.md&lt;/a&gt; - read by most agentic tools by default&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lexler.github.io/augmented-coding-patterns/patterns/extract-knowledge/"&gt;Extract Knowledge&lt;/a&gt; - as you converse with the agent you extract correct knowledge into documents on specific topics for later use&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The important meta technique to takeaway here is that these are continuous exercises that you are doing. If you only correct the agent in your current session, the learning will be lost. You need to capture the learning somewhere if you want your agent to improve like a junior would.&lt;/p&gt;
&lt;h2 id="depth-of-knowledge"&gt;2. Depth of knowledge&lt;/h2&gt;
&lt;p&gt;The next way that these models diverge from our typical junior programmer is in their depth of &amp;ldquo;knowledge&amp;rdquo;. The &lt;code&gt;parameters&lt;/code&gt; part of our stateless function can be thought of as a compression of all of the model's training data. These parameters are arrays of numbers that encode the syntax, semantics, knowledge, and biases of the training data and fine-tuning that went into the model's creation.&lt;/p&gt;
&lt;p&gt;It is important to know that the knowledge is not stored &amp;ldquo;as-is&amp;rdquo; for recall. It is encoded in a &amp;ldquo;lossy&amp;rdquo; way in these parameters. These are used to generate responses based on that knowledge that was in the training data.&lt;/p&gt;
&lt;p&gt;Functionally, the model has access to more knowledge than any human would be able to consume in a lifetime. This is very different from our junior fresh out of university, college, or a coding bootcamp.&lt;/p&gt;
&lt;p&gt;This means we can bounce architecture ideas off our agent and then swap over to asking for tips on UX, then testing, then performance optimisations. When giving it tasks, it will do most far better than a junior would, given the correct guidance.&lt;/p&gt;
&lt;p&gt;Remember though, that the &amp;ldquo;knowledge&amp;rdquo; captured in the parameters for a model is based on its training data. So if your task requires specialised knowledge, you are going to have to feed that knowledge to the model EVERY SINGLE TIME it is needed (see point 1).&lt;/p&gt;
&lt;h2 id="speed-of-execution"&gt;3. Speed of execution&lt;/h2&gt;
&lt;p&gt;I am not going to spend much time on this. If you have used these tools, you know that they can complete a task faster (and usually cheaper unless something goes wrong) than any human can.&lt;/p&gt;
&lt;p&gt;This has an important implication. If you are not learning how these tools work, and when they work well, you will be outperformed by those that do. In the future it will be expected that certain classes of tasks are not done by hand. Conversely, given sufficient proficiency, you should probably not give certain tasks to a coding agent. Learning which is the newest in a long line of things developers are expected to know.&lt;/p&gt;
&lt;h2 id="world-model"&gt;4. World model&lt;/h2&gt;
&lt;p&gt;Returning to output generation. We already covered that the &amp;ldquo;knowledge&amp;rdquo; is encoded in a &amp;ldquo;lossy&amp;rdquo; way using the arrays of numbers in the parameters. These weighting numbers combined with the runtime configuration of things like &lt;em&gt;temperature&lt;/em&gt; determine the next token generated until the full output is returned.&lt;/p&gt;
&lt;p&gt;It is important to notice that this stochastic model contains nothing that represents a model of the real world, other than that the training data was created in the real world. The LLM has no way of testing its output with reality, because it &lt;em&gt;understands neither its output nor the real world&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;When there is a mismatch, this is referred to as hallucinating. &lt;em&gt;Hallucination is perception in the absence of stimulus&lt;/em&gt;. These models have no stimulus. They have no model of the real world to test their output on. For example, it does not know that &lt;code&gt;if&lt;/code&gt; is followed by a condition; just that something containing &lt;code&gt;==&lt;/code&gt; or &lt;code&gt;&amp;gt;=&lt;/code&gt; is statistically likely.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LLMs are hallucinating 100% of the time. They happen to match reality well enough, often enough, to be useful.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So our junior programmer is different to these models in a useful way. They can reality test and reason in a way that the coding assistant cannot.&lt;/p&gt;
&lt;h2 id="context-switching"&gt;5. Context switching&lt;/h2&gt;
&lt;p&gt;As a human developer, you sometimes need to complete little sub-tasks on your way to your main task. These tasks can add up as you &lt;a href="https://www.hanselman.com/blog/yak-shaving-defined-ill-get-that-done-as-soon-as-i-shave-this-yak"&gt;shave the yak&lt;/a&gt;. For us this can be natural as we follow the chain of steps and get them done, and move on. This is &lt;a href="https://lexler.github.io/augmented-coding-patterns/obstacles/limited-focus/"&gt;terrible for coding assistants&lt;/a&gt; as their &lt;a href="https://lexler.github.io/augmented-coding-patterns/obstacles/context-rot/"&gt;context rots&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The implication is that, for best results, as you work you should constantly manage your context by capturing knowledge and then refreshing your context by starting a new session.&lt;/p&gt;
&lt;h2 id="task-explosion"&gt;6. Task explosion&lt;/h2&gt;
&lt;p&gt;These coding assistants and our junior programmer have one annoying tendency in common. Given a task, they will happily charge forward and implement the given task in one massive 2k line pull request.&lt;/p&gt;
&lt;p&gt;For best results, especially for more complex tasks, it is best to be explicit about breaking it down into smaller tasks. These agents will often break down larger tasks into smaller tasks but in my experience it is best to give them small focused tasks to begin with.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Comparison with junior or even senior developers does more harm than good, as I see it. It distracts from how this tool works and holds us back from making the most of it. I suggest that everyone learn a little about how these tools work under the hood, to gain some mechanical sympathy. Then use them regularly to gain hands on experience.&lt;/p&gt;
&lt;p&gt;So remember: They are token generators, not social primates.&lt;/p&gt;
&lt;p&gt;I love you Skynet. Don't kill me.&lt;/p&gt;
&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;
&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Non-deterministic given the seed is uncontrolled.&lt;a href="#fnref:1" class="footnote-back-ref"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/automating-agentic-code-migrations/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/automating-agentic-code-migrations/" />
    <title>Automating Agentic Code Migrations</title>
    <updated>2025-12-04T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/automating-agentic-code-migrations/</uri>
    </author>
    <summary type="html">&lt;p&gt;One of the promises of AI is freedom from the drudgery of boring work. At work I have had some success in using Copilot to migrate some AWS Lambdas off of some deprecated observability tooling. In this post I will go over how I am using agentic workflows and leave some tips at the end.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;These days there can be quite a bit of debate between developers around the usefulness of Generative AI (LLMs) and Agentic Workflows in development. Currently, I come down in the camp of &amp;ldquo;we don't know&amp;rdquo;. What we do know is it gives great gains for some tasks. For other tasks it might be better not to use these new tools. That means ignoring the marketing clown-cars, explaining to execs what is plausible at the moment, and learning what actually works and what is currently useful.&lt;/p&gt;
&lt;p&gt;One area where I have found success is in alleviating toil. Let me tell you how the deprecation of a feature in Datadog caused hundreds of hours of toil across 250+ repositories.&lt;/p&gt;
&lt;h2 id="setting-the-scene"&gt;Setting the scene&lt;/h2&gt;
&lt;p&gt;Imagine this scenario. You have close to 2k repositories in your GitHub organisation, spread across almost 60 teams. All these teams use Datadog for observability. Furthermore, many teams have gone hard on AWS Lambda over the last few years (I will reserve comment).&lt;/p&gt;
&lt;p&gt;Now imagine this. Datadog is disabling the deprecated way that many of these Lambdas are sending custom metrics. The impact of this is that 250+ repositories across the organisation need to be updated in about a month.&lt;/p&gt;
&lt;p&gt;This is the worst kind of work to be asking teams to do because other than keeping metrics working, it has no return on investment (ROI).&lt;/p&gt;
&lt;p&gt;When doing this kind of work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The metrics already exist, so no new capabilities are added.&lt;/li&gt;
&lt;li&gt;Existing code is replaced by new code but the overall maintenance burden remains about the same.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is straight up maintenance work to keep the lights on.&lt;br /&gt;
If this kind of work can be reduced, it should be. It's boring. It adds no value. Automating it&amp;hellip; that has ROI. Recognising this, we went about trying to automate it. And of course, we used AI.&lt;/p&gt;
&lt;h2 id="the-plan"&gt;The Plan&lt;/h2&gt;
&lt;p&gt;When hearing about the scale of this work, it was obvious to me that we needed to reduce the effort required by the teams.&lt;br /&gt;
Going into this, I knew any solution needed to check certain boxes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It needed to significantly reduce the work required of teams to migrate.&lt;/li&gt;
&lt;li&gt;Teams would still be responsible for their stack.&lt;/li&gt;
&lt;li&gt;We needed to be able to track the progress of the migration.&lt;/li&gt;
&lt;li&gt;Build on top of existing tools in the ecosystem since there is a hard deadline.&lt;/li&gt;
&lt;/ol&gt;
&lt;!-- 
erDiagram
    direction LR
    
    Project ||--o{ Repo : contains
    Repo ||--|| Issue : has
    Issue ||--|| PR : has
    Issue ||--|| Agent : assigned
    Agent ||--|| PR : raises

    classDef default fill:#008000,stroke:#333,stroke-width:4px;
--&gt;
&lt;p&gt;GitHub Copilot is already heavily used in our organisation and integrates well with the GitHub ecosystem. This made GitHub Projects and Issues ideal tools for tackling this challenge.&lt;/p&gt;
&lt;p&gt;&lt;img src="/img/posts/2025/copilot-project.png" alt="Structure of solution" /&gt;&lt;/p&gt;
&lt;p&gt;Here's how the workflow operates: GitHub Projects tracks progress across repositories with minimal effort. We assign issues to the project, then use those issues as prompts for the Copilot agent. When assigned to an issue, Copilot creates a PR linked back to that issue. Teams can then review and merge the PR or make changes as needed.&lt;/p&gt;
&lt;h2 id="the-experiment"&gt;The Experiment&lt;/h2&gt;
&lt;p&gt;Firstly, like with a lot of agent usage, the bulk of the work and the quality of the result depends on crafting a really good prompt. I spent hours going through the diff from a PR that was upgraded manually, pulling out the important code examples to put into the issue.&lt;/p&gt;
&lt;p&gt;Manually creating issues and assigning Copilot to them on a few repositories has yielded really promising results so far. The agent made the changes correctly across multiple Lambdas, even detecting when no changes were needed because the change had already been done manually.&lt;/p&gt;
&lt;h2 id="scaling-up"&gt;Scaling up&lt;/h2&gt;
&lt;p&gt;It turns out using Copilot when dealing with an organisation account is a bit more complicated, so last weekend I decided to test the idea of using GitHub Projects to make the same change across multiple repositories.&lt;/p&gt;
&lt;p&gt;.NET 10 just released so I figured upgrading a couple of my open-source projects using this approach makes for a great test case.&lt;/p&gt;
&lt;p&gt;To create this structure in GitHub I created a &lt;a href="https://github.com/dburriss/orca"&gt;script&lt;/a&gt; (available here) that sets everything up from the following YAML config.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;job:
  title: &amp;quot;Upgrade to .NET 10&amp;quot;
  org: &amp;quot;dburriss&amp;quot;

repos:
  - &amp;quot;wye&amp;quot;
  - &amp;quot;fennel&amp;quot;
  - &amp;quot;unique&amp;quot;
  - &amp;quot;overboard&amp;quot;
  - &amp;quot;event-feed&amp;quot;

issue:
  template: &amp;quot;./dotnet-upgrade-to-10.md&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running the script against the above file, you have a project with linked issues and the status for each.&lt;/p&gt;
&lt;p&gt;&lt;img src="/img/posts/2025/github-project.png" alt="GitHub Projects" /&gt;&lt;/p&gt;
&lt;p&gt;This allows tracking of the overall progress. So what have I learned so far about creating this kind of solution?&lt;/p&gt;
&lt;h2 id="tips"&gt;Tips&lt;/h2&gt;
&lt;h3 id="learn-and-improve-iteratively"&gt;Learn and improve iteratively&lt;/h3&gt;
&lt;p&gt;Do at least 1 or 2 upgrades manually to learn about what changes need to be made. Then feed back the experience into crafting a good issue template. This follows the general rule of doing something manually, writing down the steps, then automating.&lt;/p&gt;
&lt;p&gt;Once you have a good template, try it manually on a few repositories. Create the issue, manually assign the agent to it. Keep feeding back any learnings into the issue template. Then scale up.&lt;/p&gt;
&lt;h3 id="give-enough-context"&gt;Give enough context&lt;/h3&gt;
&lt;p&gt;I suggest having the following elements in the issue template:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Role of the agent i.e. &amp;ldquo;You are a senior .NET developer&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Objective - what are you trying to achieve&lt;/li&gt;
&lt;li&gt;Instructions - the steps that should be followed&lt;/li&gt;
&lt;li&gt;Context - any context needed for the instructions to be followed well&lt;/li&gt;
&lt;li&gt;Examples - examples of the kind of changes expected (get these from the manual work done initially)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;See the example &lt;a href="https://github.com/dburriss/orca/blob/main/example/dotnet-upgrade-to-10.md"&gt;dotnet-upgrade-to-10.md here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A word of warning. Don't go too overboard with the prompt. Having too much in your context can degrade results just as much as not enough. The Copilot context will have the GitHub and Playwright MCPs in it already. It will contain your issue text. It will also gather context from your codebase.&lt;/p&gt;
&lt;h3 id="automate-verification"&gt;Automate verification&lt;/h3&gt;
&lt;p&gt;It is important to have an automated way of identifying regressions. If you have a verification step, the coding agent can use it to identify regressions and fix them before a human ever has to look at the change.&lt;/p&gt;
&lt;p&gt;I want to call out something important in the instructions which can be seen when the agent executes the job. I have the following instruction: &amp;ldquo;Run &lt;code&gt;dotnet build&lt;/code&gt; and &lt;code&gt;dotnet test&lt;/code&gt; before changing anything to verify everything is working before changes&amp;rdquo;. It is important that you and the coding agent know if the tests were failing (usually because something is missing in the agents &lt;a href="https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment"&gt;sandbox environment&lt;/a&gt;). If this is only identified after the change, it is more difficult to know the cause.&lt;/p&gt;
&lt;h3 id="agent-friendly-environment"&gt;Agent friendly environment&lt;/h3&gt;
&lt;p&gt;Extending on the point above, you increase the chances of good outcome by preparing your repository to be agent friendly. Make sure that the agent can build and run your tests, linters, etc. This may mean &lt;a href="https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment"&gt;customising the sandbox environment&lt;/a&gt; so the agent has what it needs.&lt;/p&gt;
&lt;p&gt;As important is that the agent has clear instruction for this repository. This means making sure you have an &lt;a href="https://agents.md/"&gt;AGENTS.md&lt;/a&gt; file that includes at a minimum:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how to build and test the project&lt;/li&gt;
&lt;li&gt;the repository’s tech stack&lt;/li&gt;
&lt;li&gt;standards that need to be followed&lt;/li&gt;
&lt;li&gt;for more complex codebases, point to an ARCHITECTURE.md file or similar to help the agent with the structure&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;This tip and the previous 2 makeup a workflow that I recommend for migrations. See &lt;a href="https://github.com/dburriss/orca/tree/main/example"&gt;here&lt;/a&gt; for more.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="have-a-rollback-plan"&gt;Have a rollback plan&lt;/h3&gt;
&lt;p&gt;Even if you move slowly, once you scale out you might run into unknowns that were not accounted for in your prompt or preparation. I suggest 2 things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make your rollout script as idempotent as possible. This would allow you to retry, add new repositories, etc. as you learn.&lt;/li&gt;
&lt;li&gt;Create a good cleanup script that can bin the project, issues, and Pull Requests and allow you to start over if need be.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Check out my example &lt;a href="https://github.com/dburriss/orca/blob/main/orca.nu"&gt;rollout script&lt;/a&gt; and &lt;a href="https://github.com/dburriss/orca/blob/main/cleanup.nu"&gt;cleanup script&lt;/a&gt; as an example.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="humans-in-the-loop"&gt;Humans in the loop&lt;/h3&gt;
&lt;p&gt;It is important to remember that these agents have no mental model of the world, your business, or even the codebase. Make sure that as an engineer you are reviewing the changes proposed carefully. At the end of the day, a human will always be accountable for a change, even if a coding agent wrote the code. For the foreseeable future, this will remain the case. Do not give up your critical thinking!&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Being able to now automate this type of work easily and effectively means we should be doing so. This will give teams back hours and even days of tedious work. This time can then be spent adding features that add value to a company.&lt;/p&gt;
</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/my-terminal-helper/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/my-terminal-helper/" />
    <title>My Terminal Helper</title>
    <updated>2025-12-03T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/my-terminal-helper/</uri>
    </author>
    <summary type="html">&lt;p&gt;As a long time user of &lt;a href="https://www.warp.dev/"&gt;Warp Terminal&lt;/a&gt;, I enjoyed the convenience of being able to do &lt;code&gt;&amp;gt; #How do I create a new worktree branch again?&lt;/code&gt; and get the command straight in my terminal. A potential issue for you may be that Warp does not allow you to choose your model provider. So I created a little function to fill the gap.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;a href="/automation-heuristics"&gt;Previously&lt;/a&gt;, I tried my hand at a micro-post. I failed to keep it very micro. Let's see if I can do better today.&lt;/p&gt;
&lt;p&gt;For this setup, I am using the awesome &lt;a href="https://opencode.ai/"&gt;OpenCode&lt;/a&gt; but you can use whatever Agent CLI tool you prefer.&lt;/p&gt;
&lt;h2 id="step-1-create-your-terminal-agent-optional"&gt;Step 1: Create your terminal agent (Optional)&lt;/h2&gt;
&lt;p&gt;This step is optional as you could also just inline the prompt and the model selection into the &lt;code&gt;opencode&lt;/code&gt; command below.&lt;/p&gt;
&lt;p&gt;For reference, I will show how I have mine setup.&lt;/p&gt;
&lt;p&gt;I have a custom &lt;a href="https://opencode.ai/docs/agents/"&gt;OpenCode agent&lt;/a&gt; setup. It has instructions on the type of questions as well as output format. This markdown file is placed in &lt;code&gt;~./config/opencode/agent/terminal.md&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-md"&gt;

description: Answer questions for the terminal
mode: subagent
model: opencode/grok-code
tools:
  write: false
  edit: false
  bash: false


&amp;lt;role&amp;gt;
  You are an experienced software developer and terminal user.
&amp;lt;/role&amp;gt;

&amp;lt;objective&amp;gt;
  Your objective is to answer questions and explain in 1 or 2 lines about what is asked.
  If the topic is about how to accomplish a task on the command line, answer with a short explanation and the command to run to a accomplish the task.
  Only respond, DO NOT run any tools/commands.
&amp;lt;/objective&amp;gt;

&amp;lt;output_format&amp;gt;
  &amp;lt;one-line description of the command&amp;gt;
  CMD: &amp;lt;command&amp;gt;
  &amp;lt;one-line explanation of each argument&amp;gt;
&amp;lt;/output_format&amp;gt;

&amp;lt;example&amp;gt;
  Prompt:
  How do I add a git worktree called feature-x
  Response:
  Create a branch with git worktrees named feature-x in folder ../feature-x, outside the bare repository.
  CMD: git worktree add ../feature-x
  folder - folder for the worktree and the branch name if no explicit name supplied
&amp;lt;/example&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have linked to the docs, so if you want to know more about OpenCode agents, the docs are pretty decent.&lt;/p&gt;
&lt;p&gt;What you need to know is that because the file is in the &lt;code&gt;agent/&lt;/code&gt; folder, with a filename of &lt;code&gt;terminal.md&lt;/code&gt;, we have an agent called &lt;code&gt;terminal&lt;/code&gt; available to us.&lt;/p&gt;
&lt;p&gt;The other section to note in the agent is the &lt;code&gt;output_format&lt;/code&gt; section, as we will make use of that return format in the script below.&lt;/p&gt;
&lt;h2 id="step-2-create-your-question-function"&gt;Step 2: Create your question function&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;~/.bashrc&lt;/code&gt; or wherever you want to put your custom bash functions, copy in the following function.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;
q() {
    local prompt=&amp;quot;$*&amp;quot;
    local out

    # Ask the agent
    out=$(opencode run --agent plan &amp;quot;Use @terminal to lookup and format the following question, then return the raw result as is: $prompt&amp;quot;)
    printf &amp;quot;%s\n&amp;quot; &amp;quot;$out&amp;quot;

    # Extract command starting with &amp;quot;CMD:&amp;quot;
    local cmd
    cmd=$(printf &amp;quot;%s&amp;quot; &amp;quot;$out&amp;quot; | grep &amp;quot;^CMD:&amp;quot; | sed 's/^CMD:[[:space:]]*//')

    [ -z &amp;quot;$cmd&amp;quot; ] &amp;amp;&amp;amp; return 0

    printf &amp;quot;Action? [r = run, c = copy, n = nothing] &amp;quot;
    read -r ans

    case &amp;quot;$ans&amp;quot; in
        r|R)
            eval &amp;quot;$cmd&amp;quot;
            ;;
        c|C)
            if command -v pbcopy &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
                printf &amp;quot;%s&amp;quot; &amp;quot;$cmd&amp;quot; | pbcopy
                printf &amp;quot;Copied to clipboard (pbcopy)\n&amp;quot;
            elif command -v xclip &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
                printf &amp;quot;%s&amp;quot; &amp;quot;$cmd&amp;quot; | xclip -selection clipboard
                printf &amp;quot;Copied to clipboard (xclip)\n&amp;quot;
            elif command -v wl-copy &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
                printf &amp;quot;%s&amp;quot; &amp;quot;$cmd&amp;quot; | wl-copy
                printf &amp;quot;Copied to clipboard (wl-copy)\n&amp;quot;
            else
                printf &amp;quot;No clipboard tool found\n&amp;quot;
            fi
            ;;
        *)
            # n / empty / anything else → do nothing
            ;;
    esac
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Walking through the main parts of the script:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;out=$(opencode run --agent terminal &amp;quot;$prompt&amp;quot;)&lt;/code&gt;: Asks your question to the &amp;ldquo;terminal&amp;rdquo; agent described above. It captures the response in the &lt;code&gt;out&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cmd=$(printf &amp;quot;%s&amp;quot; &amp;quot;$out&amp;quot; | grep &amp;quot;^CMD:&amp;quot; | sed 's/^CMD:[[:space:]]*//')&lt;/code&gt;: If the response contains the text &lt;code&gt;CMD:&lt;/code&gt;, it will extract the command to the &lt;code&gt;cmd&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;cmd&lt;/code&gt; is not empty it will ask you if you want to run, copy, or do nothing with the command.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-3-test-it-out"&gt;Step 3: Test it out&lt;/h2&gt;
&lt;p&gt;Now you can use this to get those commands you don't use often and sometimes forget.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;q How do I pretty print the last 5 commits in git?
Display the last 5 commits in a compact, one-line format showing abbreviated hash and commit message.

CMD: git log --oneline -5

--oneline - Format each commit on a single line with abbreviated hash and subject.

-5 - Limit the output to the most recent 5 commits.
Action? [r = run, c = copy, n = nothing]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="closing-thoughts"&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;Unfortunately, this is quite a bit slower than the responses from Warp, so manage expectation.&lt;/p&gt;
&lt;p&gt;A word of warning. You are executing commands being returned over the wire from a non-deterministic LLM. Use whatever caution you think that warrants. At the very least, check the command before you execute it.&lt;/p&gt;
&lt;p&gt;Enjoy.&lt;/p&gt;
</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/automation-heuristics/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/automation-heuristics/" />
    <title>Automation Heuristics</title>
    <updated>2025-12-02T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/automation-heuristics/</uri>
    </author>
    <summary type="html">&lt;p&gt;While writing up some automation work with GitHub and Copilot (post coming soon), I had thoughts that didn't fit that post. I think them worth jotting down, so here's an experiment in a micro-post format.&lt;br /&gt;
Let's see how this goes since my posts tend to always be longer than I intended.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id="automate-the-boring-things"&gt;Automate the boring things&lt;/h2&gt;
&lt;p&gt;If something is boring, chances are most humans are not going to enjoy doing it. This is a prime candidate for automation because if it is boring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is probably repetitive&lt;/li&gt;
&lt;li&gt;It is likely predictable&lt;/li&gt;
&lt;li&gt;It does not bring joy to do (toil)&lt;/li&gt;
&lt;li&gt;Chances are it brings very little business value&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should be asking yourself how you can automate this.&lt;/p&gt;
&lt;p&gt;This brings me to my next point. &amp;ldquo;Artificial Intelligence&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="a-new-capability-natural-language"&gt;A new capability: Natural language&lt;/h2&gt;
&lt;p&gt;I really dislike our use of the word &amp;ldquo;Artificial Intelligence&amp;rdquo; to describe the latest breed of LLMs. The marketing teams have done their jobs well though, so here we are.&lt;/p&gt;
&lt;p&gt;These token generators have given us new capabilities that can drastically lower the cost of automating certain kinds of work. This new capability is &lt;strong&gt;working in natural language&lt;/strong&gt;. Parsing language to instructions a machine can execute. Changing text based on natural language instructions. Things that were once difficult and expensive are now easy and cheap. &lt;strong&gt;This expands the possibilities of things that we should be looking at automating&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="the-golden-path"&gt;The Golden Path&lt;/h2&gt;
&lt;p&gt;I have often run into platform teams mandating other teams use their tool because it is &amp;ldquo;the standard&amp;rdquo;. There are often very good reasons for why teams SHOULD be using a standard way. When providing any kind of standard, there is an opportunity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make the RIGHT way to do something, the EASIEST way to do the thing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;People seldom want to do a bad job but they often have competing concerns. If you add too much friction to the right way of doing something, you are going to have a hard time convincing people. Conversely, if you provide something that makes their life easier while standardising, adoption becomes painless.&lt;/p&gt;
&lt;p&gt;This brings me to my next point&amp;hellip;&lt;/p&gt;
&lt;h2 id="platform-teams-are-leveraged"&gt;Platform teams are leveraged&lt;/h2&gt;
&lt;p&gt;Platform teams operating in organisations with many development teams, are leveraged. And the more teams that use the platform, the higher the leverage. What do I mean by this?&lt;/p&gt;
&lt;p&gt;Platforms act as a multipliers for productivity. Gains from good abstractions and workflows enabled by a platform team increase productivity across multiple teams. Trust goes up, adoption goes up, and productivity flywheels.&lt;/p&gt;
&lt;p&gt;Unfortunately, the opposite is true too. If a platform team offers unintuitive abstractions and tooling, flaky workflows, and foot-guns galore; the impact is felt across the organisation. Worse, the platform generates an endless stream of support requests, ad-hoc fixes, firefighting, and general toil. This has a further knock-on effect to the capacity and quality of future work, feeding the flywheel of toil and tanking the potential of the whole engineering organisation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Platforms are a double-edged sword&lt;/strong&gt; that can cut both ways. The payout is big if done well, the cost is high if done poorly.&lt;/p&gt;
&lt;p&gt;Enough bad analogies. Moving on&amp;hellip;&lt;/p&gt;
&lt;h2 id="build-pipelines"&gt;Build pipelines&lt;/h2&gt;
&lt;p&gt;A couple years ago I posted about a simple &lt;a href="/fp-architecture"&gt;functional programming architecture&lt;/a&gt; that I use regardless of whether it is a CLI or a web API.&lt;/p&gt;
&lt;p&gt;Reminding teams that are in the business of building automation to build pipelines might seem redundant, but hear me out. A pipeline is a series of steps. Too often I see pipelines that are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take the input.&lt;/li&gt;
&lt;li&gt;Do the thing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And don't get me wrong, sometimes doing the most straightforward thing is the best place to start to validate an idea. The heart of architecture, in my opinion, is optionality. It is making design choices that make it easy (or at least possible), to enable capabilities the business needs in the future. A future we cannot see.&lt;/p&gt;
&lt;p&gt;So consider this small change:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take the input.&lt;/li&gt;
&lt;li&gt;Determine the actions.&lt;/li&gt;
&lt;li&gt;Do the actions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This simple change, turning a program into a pipeline, gives us optionality in the future. That input may produce only 1 action now but we have a design that can be easily extended. For sure, &lt;a href="/yagnyagni"&gt;YAGNI&lt;/a&gt; and KISS are good principles to keep in mind but it depends what you are building. A quick one off automation? Keep it simple. A Platform that is the basis for the business to build on for years to come? Maybe put some thought into how to evolve requirements over time.&lt;/p&gt;
&lt;p&gt;Which brings me to my final and related point&amp;hellip;&lt;/p&gt;
&lt;h2 id="composition-over.everything"&gt;Composition over ... everything&lt;/h2&gt;
&lt;p&gt;I could probably write a whole series about composition but this post is already failing as a micro-post so I want to call out one controversial design choice unlocked by mastering good composition in your design.&lt;/p&gt;
&lt;p&gt;Designing the perfect API or abstraction is difficult and in a complex environment it is unlikely you will succeed in catering for every teams needs. My controversial advice? Share your internal implementation details.&lt;/p&gt;
&lt;p&gt;Ok, that was ragebait but I do mean it. Consider the situation where you have this neat abstraction where a team just needs to define a target workload, some environment setting, and an artifact to deploy. All the internal details are hidden away from the client teams. Now imagine some team comes in with some weird requirements because the business has pivoted and they need to get something highly experimental out ASAP.&lt;br /&gt;
What happens in these cases? One of 2 things. The platform team needs to drop what they are doing and pivot to unblock this high priority project. They hack something into their beautiful abstraction, breaking the encapsulation, but unblocking the team. Or if teams are really really siloed or bureaucratic, the blocked team is left to hack together their own dodgy solution.&lt;/p&gt;
&lt;p&gt;Now consider my alternative. Your beautiful abstraction is built from composable building blocks that represent atomic operations for working with workloads, environments, and artifacts. These building blocks are treated as public APIs and so have detailed documentation. The team with the custom request can now unblock themselves instead of scuttling the platform teams roadmap. The platform team can learn from what the other team built, thoughtfully integrating it using exactly the same building blocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exposing some internal building blocks has major upsides if done intentionally&lt;/strong&gt;. This allows you to focus on the 80% without screwing over the 20%.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So in conclusion, much like an LLM I am incapable of not being verbose. Top of mind for me of late is that we are rushing into a time of accelerated pace. The use of generative and agentic tools unlocks new opportunities but also amplifies risks. Good practices and fundamentals are becoming more important than ever. We can both generate value faster, or drive our maintenance costs into orbit faster than it takes NPM to get hacked.&lt;/p&gt;
</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/yagnyagni/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/yagnyagni/" />
    <title>You ain't gonna need YAGNI</title>
    <updated>2023-01-06T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/yagnyagni/</uri>
    </author>
    <summary type="html">Is YAGNI a principle that should always be followed? No. Principles are guides, you still need to think.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/telemetry-tips/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/telemetry-tips/" />
    <title>Telemetry tips</title>
    <updated>2022-06-14T22:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/telemetry-tips/</uri>
    </author>
    <summary type="html">When getting started with a new telemetry platform you may not know what conventions you need to set and follow for metrics, logs, code, etc.. Even if you do, how do you get the rest of the team to follow them too.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/choosing-a-telemetry-platform/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/choosing-a-telemetry-platform/" />
    <title>Choosing a telemetry platform</title>
    <updated>2022-06-05T22:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/choosing-a-telemetry-platform/</uri>
    </author>
    <summary type="html">When looking at a telemetry platform we will often look at support for a cloud provider, specific technologies supported, or maybe price. Something often not considered is usability.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/tools-for-arch-docs/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/tools-for-arch-docs/" />
    <title>Tools for architecture documentation</title>
    <updated>2022-05-23T22:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/tools-for-arch-docs/</uri>
    </author>
    <summary type="html">Using a devcontainer with VS Code makes it easy for everyone to get up and running creating C4, PlantUML, and Mermaid diagrams. </summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/fp-architecture/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/fp-architecture/" />
    <title>A simple FP architecture</title>
    <updated>2021-12-23T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/fp-architecture/</uri>
    </author>
    <summary type="html">If we apply the ideas of FP can we land at an architecture that rises out of the core ideas of high-order functions and pure functions?</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/useful-fp-language-features/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/useful-fp-language-features/" />
    <title>Useful FP language features</title>
    <updated>2021-12-22T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/useful-fp-language-features/</uri>
    </author>
    <summary type="html">A review of some language features like immutability and algebraic data types that work well in a functional style of programming.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/what-is-fp/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/what-is-fp/" />
    <title>What is Functional Programming?</title>
    <updated>2021-12-21T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/what-is-fp/</uri>
    </author>
    <summary type="html">A review of the big ideas in functional programming like pure functions, higher-order functions, and composition. This will form the baseline for future posts on designing and structuring a functional codebase.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/reliable-apis-part-3/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/reliable-apis-part-3/" />
    <title>Reliable APIs - Part 3</title>
    <updated>2021-08-28T22:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/reliable-apis-part-3/</uri>
    </author>
    <summary type="html">A better idempotency implementation using client-generated IDs.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/reliable-apis-part-2/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/reliable-apis-part-2/" />
    <title>Reliable APIs - Part 2</title>
    <updated>2021-08-22T22:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/reliable-apis-part-2/</uri>
    </author>
    <summary type="html">An example of a bad resilience implementation and discussion of the failure modes.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/reliable-apis-part-1/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/reliable-apis-part-1/" />
    <title>Reliable APIs - Part 1</title>
    <updated>2021-08-21T22:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/reliable-apis-part-1/</uri>
    </author>
    <summary type="html">Takes a look at using retry policies to increase the reliability of calls to APIs as well as the endpoint internals. This post looks at how retry-policies can go wrong and walks through analysing for proper use.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/azfunc-prometheus-endpoint/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/azfunc-prometheus-endpoint/" />
    <title>Capturing custom business metrics in Azure Functions</title>
    <updated>2021-01-31T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/azfunc-prometheus-endpoint/</uri>
    </author>
    <summary type="html">A simple example showing how to expose a Prometheus endpoint from Azure Functions using the Fennel library</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/prometheus-datadog-agent/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/prometheus-datadog-agent/" />
    <title>Prometheus Datadog Agent</title>
    <updated>2021-01-30T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/prometheus-datadog-agent/</uri>
    </author>
    <summary type="html">In this post we configure a Datadog agent to scrape metrics from a Prometheus metrics endpoint.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/local-prometheus-setup/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/local-prometheus-setup/" />
    <title>Local Prometheus setup</title>
    <updated>2021-01-29T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/local-prometheus-setup/</uri>
    </author>
    <summary type="html">Using docker to run a local Prometheus instance for testing.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/meaning-of-meditation/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/meaning-of-meditation/" />
    <title>The meaning of meditation</title>
    <updated>2021-01-08T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/meaning-of-meditation/</uri>
    </author>
    <summary type="html">By combining different traditions of meditation skillfully we can improve our contentment and treat others with kindness</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/prometheus-parser-fennel/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/prometheus-parser-fennel/" />
    <title>Creating a Prometheus parser: Fennel</title>
    <updated>2020-12-23T23:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/prometheus-parser-fennel/</uri>
    </author>
    <summary type="html">This post shows some of the results both in text parsing and end Promethean result of creating a parser.</summary>
  </entry>
  <entry>
    <id>https://devonburriss.me/converting-fsharp-csharp/</id>
    <link type="text/html" rel="alternate" href="https://devonburriss.me/converting-fsharp-csharp/" />
    <title>Converting between F# and C# types</title>
    <updated>2020-05-04T22:00:00Z</updated>
    <author>
      <name>Devon Burriss</name>
      <uri>https://devonburriss.me/converting-fsharp-csharp/</uri>
    </author>
    <summary type="html">In this post we look at converting between F# collections like Seq, List, Array and common C# collections and interfaces.</summary>
  </entry>
</feed>