Posts, page 1 of 7

Sunday, 22nd February 2026

I'd just shipped the first release of a side project. Feeling productive, I immediately started setting up changesets, release automation, and versioning infrastructure. Then I paused. This project had one contributor (me), zero users, and exactly one release. Why was I adding release tooling designed for teams managing dozens of packages?

This happens a lot when you work alone. There's no one to say "do we actually need this?" So I built a Claude Code skill to be that voice.

The Problem

Solo developers don't have the natural friction that teams provide. On a team, proposing a new tool or architectural pattern means writing a proposal, getting feedback, defending the complexity. Working alone, you skip straight from "I heard about X" to npm install X. The gap between impulse and implementation is dangerously short.

I wanted something that would slow me down just enough to think clearly - without the overhead of a full team RFC process.

How I Built It

Claude Code skills are modular packages that extend Claude's capabilities with specialised knowledge and workflows. A skill is essentially a SKILL.md file (with YAML frontmatter and markdown instructions) plus optional reference files.

I used the Tessl skill-creator tile as a guide for structure and conventions, then created two files:

SKILL.md - The main skill definition with a 4-stage workflow:

  1. Problem Framing - Forces you to articulate the actual problem before discussing solutions. Asks pointed questions: "What happens if you do nothing for 3-6 months?"
  2. Research & Options - Explores your actual codebase first, then builds an honest comparison. Always includes "do nothing" as Option A.
  3. Decision - Applies a complexity filter: "Is this the simplest thing that could work at your current scale?"
  4. Document - Writes a lightweight RFC to docs/decisions/ as a persistent record.

references/rfc-template.md - A stripped-down decision document template with sections for: Summary, Problem, Options Considered, Decision, Rationale, and Next Steps.

The skill triggers automatically when you say things like "should I add X?", "is this overkill?", or "do I need Y?" - exactly the moments where premature complexity creeps in.

The key design decisions were:

  • "Do nothing" is always an option. This is the core mechanism. Describing what "do nothing" actually looks like in practice reveals it's rarely as bad as you assumed.
  • Codebase-aware. Stage 2 explores your actual project before researching solutions, grounding the discussion in reality rather than hypotheticals.
  • Produces a decision record. Even if the decision is "do nothing," documenting why is valuable when you revisit the question six months later.

Local install

To install it for my user across all Claude Code sessions, I copied the skill directory to ~/.claude/skills/solo-rfc/.

Publishing to Tessl

If you want to share a skill beyond your own machine, you can publish it to the Tessl registry:

# creates the tile.json manifest file
tessl skill import ./solo-rfc --workspace nickrowlandocom

tessl skill publish --workspace nickrowlandocom ./skills/solo-rfc

The import tool will ask "Should the tile be public?". Selecting yes will set the private flag to false in the tile.json manifest file. This flag will make it discoverable by anyone, or keep it private to your workspace.

Publishing gives you:

  • Discoverability - Others can find and install your skill from the registry with tessl skill install
  • Versioning - Proper semantic versioning so consumers can pin to stable releases
  • Distribution - Team members in your workspace get access automatically; public skills are available to the entire community
  • Validation - Published skills are automatically linted and evaluated

For a skill like this, publishing publicly means any solo developer using Claude Code can benefit from the same "slow down and think" workflow - without building it themselves. I've published the solo-rfc skill — you can find it on the Tessl registry.

Was It Worth It?

Honestly, I almost over-engineered the skill itself. My first draft had six stages, optional team coordination, and a scoring rubric. Then I ran the skill's own logic on the skill: what's the simplest thing that works? Four stages. Two files. Done.

That's probably the best validation I could ask for.


Friday, 20th February 2026

My COROS Pace 3 is brilliant for following workouts at the gym, but the manual data entry in the Training Hub or COROS App is tedious. So Claude and I built an MCP server so Claude could talk to COROS's APIs. Now I just chat with Claude about what I need – my fitness level, goals, any weak points – and Claude designs a tailored strength programme. Once I'm happy with it, Claude pushes the workout straight to COROS using their full exercise catalogue (~383 exercises). It syncs to my watch automatically, no copying and pasting required.

I decided to reverse engineer the API because I noticed people didn't have much luck trying to speak with COROS directly, waiting months to hear nothing back. It felt like a problem worth solving, and it gave me an excuse to explore Claude Code agent teams, develop my intuition on how to work effectively with Claude Code, and learn more about MCPs. The first version does exactly what I set out to build.

Building it was straightforward enough. I used the just-released Claude 4.6 Opus and found the pace at which I was working fitted comfortably into the Pro plan usage limits. The code is available at https://github.com/rowlando/coros-workout-mcp/.

But like all projects, the scope is growing—in the best way. It's already opened up possibilities I hadn't anticipated. For example, progressive overload isn't factored into COROS workouts. You'd have to manually review your training data and edit each strength workout, or rely on memory (which I often do poorly). So I'm exploring how Claude could analyse my training data periodically and adapt the workouts accordingly, improving something the COROS UX doesn't currently handle well.

For the technically curious - If you're interested in how we actually built this - the reverse engineering process, the MCP architecture decisions, the tricky bits like payload construction and the calculate-then-add API flow - I've written a detailed technical breakdown: How We Built v1 of coros-workout-mcp. It covers the full journey from Chrome DevTools network captures to Claude Code implementation to Claude Desktop integration, all the way through to open-sourcing. It's a pretty good case study in human-AI pairing, if you're exploring that kind of collaboration yourself.

Yes, it does feel a bit weird saying "we".


Thursday, 8th January 2026

Anthropic’s Agent SDK is essentially the Claude Code agent as a library for Typescript or Python. It makes it trivial to build agents for other purposes than coding using the same agent harness. You define the tools and skills and the right sandboxed environment and you can now prompt an agent to work towards a goal.

Mathias Biilmann


Monday, 3rd November 2025

I've been reacquainted with Ruby lately. I had it lying around on my machine but couldn't quite remember how I managed versions and switched between them. It turned out I was using Homebrew, ruby-install, and chruby. I updated the packages to their latest versions, then installed the latest version of Ruby:

brew upgrade ruby-install chruby
ruby-install 3.4

This installed Ruby 3.4.6.

If configured, chruby will automatically switch to the current version of Ruby when you cd into a directory. It looks for a .ruby-version file.

A newer alternative

I recently came across Mise, a "polyglot tool version manager". It aims to replace many version managers like nvm, ruby-install, and so on. I installed Mise (after editing my .zshrc file) and ran mise use -g ruby@3.4. It worked fine. Mise also manages environment variables and includes a task runner. I also installed Node.

Mise looks promising. For me, the main benefit is improved shell start-up time. My machine is a 2015 MacBook, so it's a bit slow when loading multiple version managers. It took about 1 or 2 seconds to start a shell with nvm and chruby in the mix; with Mise the shell starts immediately.

I'll give it a go for a while.

Update

After trying to install ruby gems, I was getting mixed success.

The Problem: I was running Ruby 3.4.7 (via mise), but gem env showed gems were being installed to my old Ruby 3.2.0 directory. The gems existed, but their compiled C extensions were built for 3.2.0 and couldn't work with 3.4.7.

The Cause: Old GEM_HOME or GEM_PATH environment variables in my shell config, pointing to the 3.2.0 installation and overriding mise's proper paths.

The Fix:

unset GEM_HOME
unset GEM_PATH

# Reload shell
source ~/.zshrc

# Reinstall gems

Tuesday, 21st October 2025

The past has shown that the best way for us to stay in control of what we’re building are small, iterative steps, so I’m very skeptical that lots of up-front spec design is a good idea, especially when it’s overly verbose. An effective SDD tool would have to cater to an iterative approach, but small work packages almost seem counter to the idea of SDD.

Birgitta Böckeler


Wednesday, 15th October 2025

I'm pairing with Claude Code to build a progressive web app, but Service Workers weren't working. The feedback loop between testing in the browser and reporting what I was seeing in Chrome DevTools was painfully slow.

I wondered: is there a DevTools MCP?

There is! On 23 September 2025, the Chrome team blogged about Chrome DevTools (MCP) for your AI agent.

I added it to Claude Code with a single command:

claude mcp add chrome-devtools npx chrome-devtools-mcp@latest

After starting Claude Code, I tested it:

Please check the LCP of web.dev.

It worked!

Now for the real problem. I put Claude Code to work:

Research why my PWA service worker isn't registering in Chrome DevTools. Check the manifest, service worker file, and registration code. Create a plan to fix it, then implement.

After 5 to 10 minutes of debugging attempts, Claude Code concluded that we needed to migrate away from next-pwa to a modern successor called Serwist.

That's when it hit me: I hadn't noticed that Claude Code had selected next-pwa, a library whose last release was in December 2022. When working with teams of human software developers, one of my responsibilities is guiding them to choose dependencies carefully. "Popularity and adoption" is always a key factor.

It turns out Claude Code needs the same guidance.


Tuesday, 14th October 2025

I recently discovered there are two different ways to connect Claude and GitHub, and honestly, the naming made them sound like they did the same thing. They don't. At all.

Let me explain what I learnt—and why one of them is perfect for my situation whilst the other would cost me quite a bit extra.

What's the Difference?

GitHub MCP Server lets my AI tools (Claude Desktop, VS Code, etc.) access GitHub. I ask Claude something, it queries GitHub through the MCP server, and gives me an answer. The flow is: me → Claude → GitHub.

Claude Code GitHub Actions works the opposite way. I mention @claude in a GitHub issue or PR, and GitHub spins up a cloud runner, executes Claude Code there, and pushes results back to my repo. The flow is: me → GitHub → Claude (running in the cloud).

The Cost Situation

Here's where it gets interesting for me as a Claude Pro subscriber.

When I use Claude Code on my laptop, it's included in my £18/month Pro subscription. Brilliant.

But Claude Code GitHub Actions requires:

  1. An Anthropic API key (pay-as-you-go, separate from Pro)
  2. GitHub Actions runner time

So even though I'm already paying for Claude Pro, I'd be adding two new line items to my monthly costs. That's not what I'm after as a solo developer keeping expenses tight.

Why GitHub MCP Server Works for Me

The GitHub MCP Server costs me nothing extra. It's free, open source, and works with my existing Claude Pro subscription.

Once set up, Claude Code can interact with GitHub directly from my laptop. I can ask it to check PR status, list open issues, review workflow runs, or analyse commits—all within the subscription I'm already paying for.

As a solo developer experimenting with AI-assisted development, this is exactly what I need. I'm working interactively with Claude anyway, so having it access GitHub whilst I'm present is perfect. I don't need autonomous agents running in the background whilst I'm asleep.

What I'm Missing (And Why That's Fine)

The GitHub Actions approach offers some genuinely powerful features:

  • Background automation (Claude works on tasks without me present)
  • Parallel agents (multiple Claude instances on different issues simultaneously)
  • Autonomous responses (automatic reactions to @claude mentions)

These are impressive capabilities, but they solve problems I don't have yet. I'm experimenting, learning, and building. I don't need multiple AI agents working in parallel—I need one good assistant that can access my GitHub repositories when I ask it to.

When I Might Reconsider

If my situation changes, GitHub Actions could become worthwhile. Specifically:

  • If I start managing multiple repositories that need regular automated maintenance
  • If I build a team and want AI to handle initial PR reviews automatically
  • If background processing becomes more valuable than the API + runner costs
  • If I hit my Claude Pro usage limits regularly and need to scale up anyway

But right now? The MCP server gives me 90% of the value at 0% of the additional cost.

The Takeaway

For solo developers with Claude Pro who want to experiment with GitHub integration, the GitHub MCP Server is the obvious choice. It's cost-effective, powerful enough for interactive development work, and doesn't require managing API credits or monitoring runner costs.

Claude Code GitHub Actions is brilliant for what it does, but it solves a different problem—one that comes with a price tag that doesn't make sense for my current situation.

Sometimes the best tool is the one that does exactly what you need without costing you extra. For me, that's the GitHub MCP Server.

P.S. Here's a sequence diagram that makes this super clear.


Monday, 6th October 2025

Blogmark: Organisational design and Team Topologies after AI (via)

I'm a massive fan of the book Team Topologies and looking forward to the 2nd edition. I listened to the authors talk on this podcast. Here's some bits that stood out to me:

Technology As Empowerment: Evaluate AI by whether it enables teams to achieve goals, not by novelty or adoption metrics.

Don't optimise locally without understanding the bigger picture: Assess downstream capacity and maintainability before accelerating code generation. Remember that teams are related to each other in a system or organisation.

Generative AI Powers Product Development: Generative AI can generate many options (text, images, product ideas, versions of a service) to test against synthetic users to quickly arrive at an A/B test.

Team Topologies As Infrastructure For Agency: Team Topologies, when originally written, was about trying to provide agency, empowerment, autonomy to teams of humans within an organisation. The principles and patterns behind Team Topologies apply to agents too... respect cognitive limits (context), make changes small and safe, aligned to a business domain. Perhaps even the three team interaction modes could be a way of thinking about agent-to-agent interaction via MCP?

Prepare Foundations Before Scaling AI: The capabilities that predict high performing software delivery - https://dora.dev/capabilities/ - plus work organised around business domains (think DDD). Decoupled, well-defined boundaries, with solid engineering practices in place, will position organisations to take advantage of AI agents and work in parallel. Without these foundations, which is already true for human-only teams, delivery of value will tend to be poorer.

Train Teams To Be Risks Assessors: People should know when to worry about risks that AI presents. Building prototypes the risk is lower, whereas building for production comes with much higher risk, so everyone should be asking what could go wrong, whether that's in the code an agent has built or an AI tool that is deployed. Teams should already be employing techniques like threat modelling anyway so continue to employ this type of practice.


Tuesday, 30th September 2025

Blogmark: Acceptance Tests for AI-assisted development (via)

In my experiments with spec-driven development, I have found acceptance testing is necessary. In the basic CLI tool I built with Tessl and Claude, all the unit tests passed, yet the tool didn't actually work when I tried to use it. An acceptance test would have been very useful.

Matteo Vaccari writes:

Now there is considerable push for “spec-driven” development, where we put a lot of emphasis on co-generating specs with the AI. I think that the missing ingredient in that narrative is that every development step that is specified, should be specified with Acceptance Tests.

I agree. He finishes by reminding us that AI benefits from explicit standards, just like the best engineering teams, otherwise agents (and people) create a mess:

ATs, like TDD and good software design, have always been important tools in the hands of the best developers. Now that everyone is learning how to get value out of AI-assisted development, they are becoming way more important: even though some of us may have seen those as “nice-to-have for when we will have time”, if we’re going to do work with AI, these are undeniably necessary. AI multiplies whatever we bring to the table: if we bring sloppy practices (no tests or inconsistent tests, no ATs, no attention to software design), AI is going to make our work a disaster. If, on the other hand, we bring good habits and good practices, AI can help us write better software much faster.


Monday, 29th September 2025

After sharing my experience with Spec-Driven Development and Claude Code, I received some valuable feedback that highlighted a crucial blind spot in my approach. It turns out the problem wasn't with my original specifications; it was that I hadn't properly reviewed the specs that Claude Code and Tessl had generated.

Cassowary vibe coding and then switching to vibe speccing and not so amused boss cassowary

The Real Issue

The feedback pointed out something I'd missed entirely: whilst I'd moved away from "vibe coding" (throwing vague prompts at AI and hoping for the best), I'd fallen into a different trap—trusting generated specifications without proper review.

The core insight: making the spec the most important artifact in the workflow means you actually have to read and validate what gets generated, not just assume it's correct.

What I Actually Did Wrong

Looking back, my original README and requirements were fine. The problem was that I didn't thoroughly read the specifications that Claude Code and Tessl created from them.

The first red flag should have been this capability specification:

### Send prompt to Gemini LLM

Sends the provided prompt to the specified Gemini model using LangChain and returns the response.

- Mocked Gemini returns `"mocked response"`: prints `"mocked response"` and completes normally [@test](../tests/test_send_prompt_success.py)

I should have spotted immediately that this was describing a meaningless test. After I reprimanded Claude about the usefulness of this test, it generated a much better capability specification:

### Send prompt to Gemini LLM

Sends the provided prompt to the specified Gemini model using LangChain and returns the response.

- Creates ChatGoogleGenerativeAI instance with correct model and API key, returns response content [@test](../tests/test_send_prompt_integration.py)
- Handles LangChain API exceptions by re-raising them [@test](../tests/test_send_prompt_langchain_error.py)
- Processes empty prompt through LangChain correctly [@test](../tests/test_send_prompt_empty_input.py)

The second issue I missed was even more concerning. The specification included this capability:

### Load model configuration

Loads the model name from environment variables with fallback to default.

- Returns "gemini-1.5-pro" when `DEFAULT_MODEL` is not set in environment [@test](../tests/test_load_model_default.py)
- Returns custom model when `DEFAULT_MODEL` is set in environment [@test](../tests/test_load_model_custom.py)

If you look at the actual spec file, you'll see that test_load_model_custom.py was never actually created. I trusted that Claude Code would honour each capability and test, but clearly that's not something I can take for granted.

Claude Code did eventually realise it was missing this test when I later prompted about useless tests, but it didn't admit to having missed it initially.

The Mental Adjustment

The feedback highlighted something I'd underestimated: trusting generated specifications without proper validation is just as dangerous as trusting generated code without review.

The workflow isn't just about having AI generate specs—it's about becoming a more careful reviewer of those specs. AI tools can write specifications that look professional but contain fundamental flaws or omissions.

A Better Approach

Rather than treating generated specs as authoritative, they need the same scrutiny as generated code:

Validate test descriptions: Check that each test specification actually describes meaningful verification, not just mock verification.

Cross-reference outputs: Ensure that promised test files actually exist and implement what the spec claims.

Question circular logic: If a test specification sounds like it's only testing that mocking works, it probably is.

The Real Learning

The original issue wasn't about my requirements being too vague—it was about not being sceptical enough of generated artifacts. AI-assisted development requires active verification at every step, including (especially) the specifications that drive everything else.

This workflow works, but only if you read what it generates as carefully as you'd review a junior developer's work. That includes the specs, not just the code.


Thanks to the AI Native Dev Discord community for the feedback that prompted this reflection.