• lately, I find myself mostly writing markdown plans instead of code
  • in these plans, I
    • verbalize my thoughts on what the implementation should look like
    • link relevant files, which I found during my research phase
  • using GitHub Copilot, I can then turn these plans into code
  • essentially, the artifact of my work has inverted:
    • previously, I thought about a plan, and wrote code
    • now, I write a plan, and think about code (when reviewing it)
  • my main tool for thought has changed as well:
    • previously, I thought by wrangling code
    • now, I just think; Copilot wrangles
    • this is possible because the feedback-loop has become quicker, since coding can be largely automated
  • I often read discussions where developers say “agentic coding just doesn’t work”
  • that’s why, in the next few bullet points, I’ll share my current workflow in an attempt to explain the black magic
  • first of all, write a spec including:
    • what do you want to implement?
    • how can it be achieved?
    • where to make the changes?
  • take your time; for a medium sized task, this usually takes me 10-30 minutes
  • my current spec template looks like this:
    ### Description
    *1-3 high level sentences on what i want to implement*
     
    e.g.: "Users want to... Therefore we need a button to..."
     
    ### Knowledge
    *all information required for the implementation as bullet points*
     
    e.g.:
    - you can get the data from /api/foo/bar
    - the result is a json object with a `bar` key
     
    ### Plan (high level)
    *high level implementation plan as ordered list*
     
    e.g.:
    1. add the button to the [overview page](...)
    2. write component tests
    3. write api tests
  • before moving on, I verify the spec is understandable without extra context:
    • this is like checking code for un-imported dependencies
    • however, here a dependency means missing information rather than code
  • ideally, this spec contains only task-specific information
  • additional context should be supplied automatically via instruction files
    • instruction files are markdown files automatically sent with your prompt based on file-path patterns (applyTo regex in frontmatter).
  • my instruction file structure looks like this:
main.instructions.md          # applyTo: '**'
frontend.instructions.md      # applyTo: 'frontend/**'
backend.instructions.md       # applyTo: 'backend/**'
testing.backend.instructions  # applyTo: 'backend/**/test/**
serviceA.backend.instructions # applyTo: 'backend/serviceA/**
serviceB.backend.instructions # applyTo: 'backend/serviceA/**
  • this dynamic prompting approach greatly reduces the amount of information I have to add to my spec prompt
  • once the spec is done, I use Copilot to compile it into its own low-level implementation plan
    • it’s like a dry run
    • the resulting plan may verbose, but reviewing it is usually worth it:
      • often, Copilot gets something wrong
      • errors may also reveal unclear parts or even mistakes in my original spec
      • when that happens, I refine the spec and re-generate the implementation plan
  • next, I feed the implementation plan to Copilot, which incrementally:
    1. Adds logic & tests.
    2. Runs tests
    3. If they fail, it fixes issues and reruns tests.
    4. If they pass, it moves to the next logic/test.
  • usually, the generated code only needs minor adjustments
    • this is the point, where I actually write code
  • other than that, source code is now mostly immutable by my own hands
    • just like a mechanic wouldn’t attach a door with his bare hands, I don’t implement new code without Copilot anymore
  • essentially, agentic coding requires completely thinking throught tasks before implementing them
  • furthermore, you need to be very conscious of what information is necessary and how it can be accessed
    • this is a skill known as context engineering
    • I hope you learned a bit about it in this post ^^