CCEPL-driven development

Hrishi Olickel·

Until recently, my favourite method of coding - the one most conducive to flow states - was REPL-driven, instead of Test or Eval driven.

This is not a new idea at all. Lisp (I believe) really pioneered the concept11REPL-driven development (Mike Levins, 2020)1REPL-driven development (Mike Levins, 2020), and there are strong echoes of the same process in modern notebooks like Jupyter and Hex.

The central conceit is simple. Instead of building the entire program before turning it on, you interact with the program as you build it. You incrementally add code, testing each new part for behaviors in real-time.

REPL stands for Read-Eval-Print-Loop. If you're on a modern Mac or Linux machine, running python in the terminal will drop you straight into a one.

The process is simple: You open up a REPL (which a long time ago for me was Node or Devtools, today it might be 'ts-node') and start adding in your dependencies. You write your code in the REPL (or you write it elsewhere and paste it in), verifying that core functionality works as you go. When you run into a problem, you have an interactive environment to figure out what went wrong.

REPL-driven-development can be addictive. It suits a particular type of project - which today is the lion's share of programming - where you rely on building plumbing to connect dependencies instead of building everything from scratch, and half your job is getting large pieces of code to work together.

This is how I've worked forever. I remember writing about it six years ago while talking about the role of intent in devtools.

Then we got coding agents.

Chat-Eval-Print-Loop

For me, Claude Code was the first agent that really changed my workflow. CC (or Codex, or Gemini CLI) are all the same familiar REPL, except with an AI in the middle. The old patterns fit perfectly, except for larger dependencies, a huge unpredictable AI in the system, and even more variable outputs.

I am sure I'm not alone in the joy of making something new happen with CC - in wresting with the agent, writing scripts and teaching it new things until you can make something happen. I am also sure I'm not alone in the despair of not being able to repeat it - of feeling like the model today is just dumber, or you didn't say the right words the right way - and facing the uphill struggle of doing that same thing twice.

In the world of REPL-driven-development, here's how things go:

  1. You load up Node (or python or lisp).
  2. You write your code.
  3. You interact with the program as you build it.
  4. What works gets pulled into a script or file, to be loaded again if you restart the REPL.
  5. You incrementally build the entire program.
  6. You ship the program. To use this program again, you don't need the REPL, you just need the program.

With coding agents, the agent itself is often part of the final product - or it needs to be for complex tasks. The output code alone is limited in what it can do.

There hadn't been a way to move forward, so we built Strandweave.

Shipping the magic

A long time ago I talked about Chat → Play → Nest → Loop as a method for building AI applications. Turns out there might be a new process, one where the agentic loop is the central element instead of the LLM call. This is now a process I've followed many times over, across months:

  1. I create a new folder for my thing. I make a folder and a file, the file's called process.md and the folder is called workspace.
  2. I open up Claude Code. Load it with process.md.
  3. I start doing my thing, slowly. Let's say we want to convert a Latex report to a typst report (something I did the other day).
  4. Load in the latex report and ask for the conversion, see what fails.
  5. When it fails, we have three options depending on why:
    1. If we failed because we didn't have the right information (in this case it could be some specific part of typst), we add documentation or a note to workspace. We place a marker in process.md referencing where it is.
    2. If we failed because the model wrote code that was fragile and took a lot of turns to solve, we take the fixed code (in this case that might be something to parse command line arguments) and place it in workspace.
    3. If we failed because the model needed more context, or it went off in the wrong direction, we place appropriate instructions in process.md.
  6. We restart Claude Code from step 2. This can feel like you're going backwards at first, but it pays off - I promise.
  7. The real magic happens when you're done. Once you are, you simply need to write a strand.json file linking in the process.md file and the workspace.

To repeat this any time in the future, you simply need to run the strand (what we call programs in Strandweave) with new data, and the phase config. In addition,

  • You can reuse this process in much larger phase configs if you need a report conversion.
  • If you run into issues in the future, you can fix this phase config and you've improved it permanently.
  • You can give anyone your strand and they can now do what you did.

The promised land

It's hard to explain how magical this felt the first time around. Every other time, we've had to either build a new agentic system from scratch just to export some capability (like extracting citations from research papers and summarising them, or translating between languages by using a multi-stage process), or wrestle a new agentic backbone in an automation software.

If you've tried to do this, you'll know that switching agentic backbones (the thing that runs the Chat-Eval-Print-Loop) is non-trivial. System prompts, edit tooling, a lot of things change at the same time that causes unexpected behavior.

Strandweave does not have this problem because it runs CC under the hood. We've already been testing Codex, Gemini CLI and Cline through the shims generated by strands. This means that you can simply work the way you do (with minimal modification) and ship once something works, and run different parts of a sequence inside different harnesses.

We call it Claude Code → Eval → Print → Loop-driven-development, or CCEPL. Build it in the CCEPL, seal it and ship it in Strandweave.

Pros and cons

I'll give you one new argument against, and for:

The counter-argument for this method is the same one we've always had for Docker, or interpreted languages: it's inefficient. If you wanted to do it the best, fastest and most optimized way, you should ideally be writing C, or bare metal instead of relying on a garbage collector. In our case, building your own backbone specific to the use-case will get you better performance and control, at the cost of 10x-ing your development time and cost.

However, here's a new argument for CCEPL; we believe that models and their backbones evolve together - Claude Code is a really good example. It is non-trivial to build solid edit functionality, good system prompts optimized for specific models, shell command access and process management that can function over thousands or millions of tool calls.

Strandweave gets to take advantage of the billions in development spent on optimizing Claude Code as harness (or Codex or Gemini). We get to ship faster.

We call it Claude Code → Eval → Print → Loop-driven-development, or CCEPL.