The Archaeologist’s Copilot
The Archaeologist’s Copilot
Restoration of a 20-year-old Java “Big Ball of Mud” using AI and Docker
This article explains the approach I used to modernize a Java 1.5 codebase that no longer<br>built reliably on modern machines. My early use of LLMs gave me plausible answers that<br>did not hold up in the codebase. Progress came when I grounded the process in evidence,<br>using AI to support analysis, validation in a stable Docker environment, and gradual<br>refactoring protected by tests. The main takeaway is practical: AI was most useful when<br>constrained by evidence, clear roles, and a step-by-step modernization strategy.
16 July 2026
Nik Malykhin
Nik is an Israeli software developer and Thoughtworker. In<br>addition to his interest in applying AI to software<br>development, he believes that the combination of discipline<br>and human collaboration is becoming even more important in<br>the AI era.
Contents
The “Tourist” Trap
The Hallucination of Competence
The Lesson
Phase I: The Analysis
The Archaeologist Prompt
Finding 1: Carbon Dating the Artifact
Finding 2: The “Transliteration” Trap
Finding 3: Lying Tests
The Decision: Containment over Repair
Phase II: The Wrap
The Mission: Brownfield Restoration
The Pivot: The “Time Capsule” strategy
The “Wet” Test: Bending Reality
Phase III: The Lift (Unwrapping the Artifact)
The Hardware Rationale
The “Java 17 Trap”
The Execution: Mapping the Legacy Structure
The “Lying Tests” Discovery
Hardening the Baseline
The AI-Compiler Feedback Loop
Phase IV: The Refactor
Mastering the Craft
The TestContainers Trap
The Final Sweep: Concurrency & Stress Testing
Conclusion: The Handover
Scrubbing the Environment
The Project Roadmap: README.md
Final Thought: The Augmented Archaeologist
The “Tourist” Trap
We all have “that” repository in our organization. The one written in<br>2005, built with Ant, using Java 1.5. It hasn't been compiled since the<br>Obama administration, and it certainly doesn't run on your new Apple<br>Silicon MacBook.
When I inherited this “Brownfield” project, the temptation is to treat<br>Generative AI as a universal translator. I paste the code into an LLM and<br>ask the most natural question in the world: “How do I run this?”
This is what I call the “Tourist Prompt.” Like a tourist visiting<br>ancient ruins, I am asking for a guided tour and a gift shop souvenir. I<br>want a happy path.
In our experiment, I tried exactly this. I opened a chat with a<br>standard LLM and asked:
“Hi, I need to start working with this library. Can you please act as a<br>Senior Developer and help me get started? Read the repo and give me a<br>high-level summary... and a simple 'Hello World' code example.”
The Hallucination of Competence
The AI acted like a polite, eager-to-please tour guide. It scanned the<br>README.txt, ignored the decades of dust, and confidently generated a<br>modern “Starter Kit.” It gave me a pristine build.gradle file and a<br>clean HelloBlobStore.java. It told me exactly how to connect to the<br>database. On the surface, it looked like a miracle.
The AI-generated build.gradle
plugins {<br>id 'java'
group = 'com.legacycorp.blobstore'<br>version = '1.1'
sourceCompatibility = '1.8'<br>targetCompatibility = '1.8'
repositories {<br>mavenCentral()
dependencies {<br>implementation 'org.apache.commons:commons-pool2:2.11.1'<br>implementation 'log4j:log4j:1.2.17'<br>testImplementation 'junit:junit:4.13.2'
test {<br>useJUnit()
The result was a lie — and a structural lie at that. By generating a<br>modern build file, the AI merely painted a fresh coat of paint over a<br>crumbling structural wall.
First, it hallucinated dependencies by suggesting commons-pool2<br>(v2.x) when the legacy code actually relied on org.apache.commons.pool<br>(v1.x). Because these libraries have completely different APIs, blindly<br>running the AI's code would have crashed the build with “Class Not Found”<br>errors, sending me down a frustrating rabbit hole of debugging “modern”<br>code that was never meant to be modern.
Next came the structural gaslighting. The AI confidently assumed a<br>standard Maven layout src/main/java, completely ignoring the reality<br>of a non-standard Ant structure java/com/legacycorp.... It was describing<br>the reality it wanted to see, not the one that actually existed.
Finally, it hid the underlying rot. Its pristine “Hello World” example<br>featured PooledBlobStoreImpl, omitting the fact that the core<br>implementation SimpleBlobStoreImpl wasn't even thread-safe, the<br>error-handling code routinely swallowed exceptions, and the so-called<br>“Unit Tests” were actually integration tests that required a live MySQL<br>database to run.
The Lesson
AI defaults to optimism. When I ask “How do I run this?”, it assumes I<br>can run it. In a restoration mission, optimism is fatal.
If I had<br>followed the Tourist path, I would have started refactoring<br>immediately—changing List to ArrayList<> or adding Generics—likely<br>breaking hidden behaviors I didn't fully understand. I would have been<br>making blind changes to a fragile...