This article is part of “Exploring Gen AI” . A series capturing Thoughtworks technologists' explorations of using gen ai technology for software development.
Giles is CTO for Europe, Middle East and India at Thoughtworks. He has over 25 years experience in engineering and technology leadership across technologies from mobile to AI and industries including retail, fintech and healthcare.
As part of getting to grips with the new world of agentic engineering, I built an application to support my work. It’s a sophisticated app: high-quality web UI with dynamic refresh and look-up, modals and auto-save, integrations to external systems, machine learning and text analysis, background jobs, and a proper environment setup with fully automated deployment. It’s approximately 150,000 lines of code, primarily in Rust (~120 kLoC) with the remainder in TypeScript and Terraform.
This was entirely written by agents. Mostly Claude Code, and some use of Cursor. I didn’t read or review any of the code, except occasionally, out of interest.
While building the application, I could see some things going awry. After watching an edit to line 4,000 of a file scroll by in the terminal, I had a closer look. The data access layer had grown to over 6,000 lines. As more features landed, this continued to grow. Every query, read or write, repeated the same HTTP request setup, the same JSON encoding and decoding. Eventually, it reached 17,155 lines. In a single Rust file.
An experiment in refactoring
The 17,155 line file was the entire data access layer. A single, self-contained module. Reviewing the code, there was no de-duplication, no internal language, limited extraction of functions, and very little extraction of classes. It did have a clear boundary with an interface to preserve. It was a great target for refactoring.
The goal of refactoring an agentic code base is to spend tokens now in refactoring to make token consumption for future work lower. An experiment should be able to show that as this file was refactored the token cost of making separate feature implementations in this code base would decrease.
Precisely because agents never learn this was now possible to run as an experiment. I could prompt a fresh agent to make exactly the same change after every refactoring stage. Unlike a human engineer, the experiment would not be tainted by learning from previous steps.
Create an overall refactoring plan, following strict refactoring discipline. Craft a representative change, described in a single prompt. Establish a baseline cost of change: in a sub-agent, execute that prompt, including asking the sub-agent to report token consumption. Throw away the change. In a loop: Apply a single step of the overall refactoring. In a sub-agent, execute exactly the same change receiving the token cost of the change. Throw away the change. Record all token costs, time to execute the change, and lines of code after each step of the refactoring, including the baseline.
... continue reading