I've been writing code with LLMs since summer 2024. The way I use them has changed quite a bit in that time, so this post is an attempt to document those changes and how I currently use LLMs to build and maintain Boords.
I've been a web developer for almost 20 years now, and I can confidently say using LLMs, in particular Claude Code, is the biggest productivity boost I've ever experienced.
The pace things are moving this will likely quickly become outdated, and I'm not suggesting any of these approaches is the single best way, just documenting what works for me.
My current workflow is primarily Claude Code with Cursor as my IDE (mainly for the AI-enabled autocomplete at this point), and Warp as my terminal.
Prerequisites & Principles
To get the most out of using LLMs for development work, follow the same principles you would when working with another developer.
- Know how to code. You MUST know what good looks like. If you don't know what you're looking at, it's impossible to know if there are errors or edge cases that will cause issues later on.
- LLM = very well-read junior developer. Good results require writing effective product requirements documents and clearly articulating what you want.
- Work in small chunks: Create small, well-defined tasks, review output, then move on.
- Commit early, commit often. Work on a feature branch, ensure everything is committed before starting a new task.
Level 0 - Use a web-based LLM
In short: copy/paste code snippets into Claude/ChatGPT and ask for changes.
This is a good way to get started, but it's difficult to do anything complex or provide additional context and results are often mixed. I used this approach for a few months so it can work, but it's not a good long-term solution for serious development work.
Level 1 - LLM-enabled IDE (e.g. Cursor)
A step up from web-based LLMs, Cursor embeds AI support into VSCode. It's much easier to add project context and the AI-enabled auto-complete was a true "Aha" moment for me.
Cursor is great for small changes and general grunt work, and I still use it together with Claude Code (see below), but code quality varies for more complex tasks, bug fixes etc.
Level 2 - Claude Code
Claude Code is a command-line interface and is, at time of writing, state of the art for AI-enabled development. It has full project context from the outset and better workflow support for complex tasks.
At it's simplest, you can fire up the CLI and enter a task, e.g.:
$ claude
---
Extract ComponentX from path/to/BigComponent.tsx into a new file called ComponentY.tsx
Results with CC tend to be much better that Cursor et al, largely due to how good the underlying model is at interpreting/writing code, but fundamentally the workflow is the same.
Level 3 - Claude Code with Task Management
Eventually you'll reach a point where you find it cumbersome to write complex tasks directly in the CLI. For more complex work (or indeed parallel tasks), you need a task managament system.
Claude Code automatically creates a .claude/
directory in your project root after setup.
To manage tasks, create a new .docs/
folder with subfolders for todo
, plans
, and reference
, and a commands
subfolder in .claude
:
Claude Code Structure
.claude/ ← auto-generated by claude on project setup
-- commands/ ← custom commands
.docs/
-- todo/ ← your markdown tasks
-- plans/ ← written by claude
-- reference/ ← reference documentation
...(the rest of your codebase)
Workflow overview
- Write a Todo. Write a markdown file in
.docs/todo/
outlining your change. - Ask Claude to create a plan. Ask Claude to create a plan (NOT to implement) the task you've just written in
.docs/todo/my-task.md
. This will create a new file in.docs/plans/
, e.g..docs/plans/my-task-plan.md
- Review the plan (new session). Manually review the plan to catch any obvious errors. You can also ask Claude to review it's own plan in a new session.
- Implement the plan (new session). Ask Claude to implement
.docs/plans/my-task-plan.md
- Review the implementation (new session). Ask Claude to review changes in light of the plan, looking for areas of significant improvement, edge cases, etc.
Custom Commands
To implement this it's helpful to create custom commands in your .claude/
subfolder:
.claude/
-- commands/
---- plan.md
---- plan-review.md
---- implement.md
---- implementation-review.md
Each markdown file contains instructions on what to do at a given stage. For example, my plan.md
file looks like this:
You are a senior full-stack software engineer. Your objective is to write a markdown document in `/.docs/plans` that specifies how to achieve the following. Include a link to this original todo in your plan.
Plan the task in the $ARGUMENTS markdown file located in /.docs/todo
ultrathink.
Note: ultrathink
triggers the deepest thought the model is capable of. It's slower, but good to use as a default.
You can then use these commands in the cli at each stage of your workflow, e.g.:
$ claude
---
/project:plan .docs/todo/my-task.md
Once this is done, you'll have a new plan document and you're ready to review and move on to the next stage of the workflow:
...
.docs/
-- todo/
---- my-task.md
-- plans/
---- my-task-plan.md ← Claude's plan
...
Now you can move on to the review and implement stages of the workflow.
Advanced: Multiple Claude Instances
Once you're comfortable with the workflow, you can run multiple Claude Code instances at the same time. I generally limit to two, one planning, one executing, while I'm writing a new todo.md
.
Next Steps: Using MCPs
MCP (Model Context Protocol) is a way for LLMs to use external services, APIs, etc. Setting up an MCP is beyond the scope of this article, but a couple that I use regularly are:
- zen-mcp-server. Allows Claude to talk with other LLMs (e.g. Gemini, o3) to come up with the best possible solution to a problem. New favourite.
- Rollbar. Gets information about errors in our Rails stack, and (with a custom command) can identify the source of the error and propose a solution.
- context7. Fetches up-to-date documentation for a given library.