The Archaeologist's Copilot A developer modernized a 20-year-old Java 1.5 codebase using AI and Docker, learning that LLMs produce plausible but unreliable answers when not grounded in evidence. The project succeeded by constraining AI with validation in a stable Docker environment and gradual refactoring protected by tests. 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 built reliably on modern machines. My early use of LLMs gave me plausible answers that did not hold up in the codebase. Progress came when I grounded the process in evidence, using AI to support analysis, validation in a stable Docker environment, and gradual refactoring protected by tests. The main takeaway is practical: AI was most useful when constrained by evidence, clear roles, and a step-by-step modernization strategy. 16 July 2026 The “Tourist” Trap We all have “that” repository in our organization. The one written in 2005, built with Ant, using Java 1.5. It hasn't been compiled since the Obama administration, and it certainly doesn't run on your new Apple Silicon MacBook. When I inherited this “Brownfield” project, the temptation is to treat Generative AI as a universal translator. I paste the code into an LLM and 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 ancient ruins, I am asking for a guided tour and a gift shop souvenir. I want a happy path. In our experiment, I tried exactly this. I opened a chat with a standard LLM and asked: “Hi, I need to start working with this library. Can you please act as a Senior Developer and help me get started? Read the repo and give me a 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 README.txt , ignored the decades of dust, and confidently generated a modern “Starter Kit.” It gave me a pristine build.gradle file and a clean HelloBlobStore.java . It told me exactly how to connect to the database. On the surface, it looked like a miracle. The AI-generated build.gradle plugins { id 'java' } group = 'com.legacycorp.blobstore' version = '1.1' sourceCompatibility = '1.8' targetCompatibility = '1.8' repositories { mavenCentral } dependencies { implementation 'org.apache.commons:commons-pool2:2.11.1' implementation 'log4j:log4j:1.2.17' testImplementation 'junit:junit:4.13.2' } test { useJUnit } The result was a lie — and a structural lie at that. By generating a modern build file, the AI merely painted a fresh coat of paint over a crumbling structural wall. First, it hallucinated dependencies by suggesting commons-pool2 v2.x when the legacy code actually relied on org.apache.commons.pool v1.x . Because these libraries have completely different APIs, blindly running the AI's code would have crashed the build with “Class Not Found” errors, sending me down a frustrating rabbit hole of debugging “modern” code that was never meant to be modern. Next came the structural gaslighting. The AI confidently assumed a standard Maven layout src/main/java , completely ignoring the reality of a non-standard Ant structure java/com/legacycorp... . It was describing the reality it wanted to see, not the one that actually existed. Finally, it hid the underlying rot. Its pristine “Hello World” example featured PooledBlobStoreImpl , omitting the fact that the core implementation SimpleBlobStoreImpl wasn't even thread-safe, the error-handling code routinely swallowed exceptions, and the so-called “Unit Tests” were actually integration tests that required a live MySQL database to run. The Lesson AI defaults to optimism. When I ask “How do I run this?”, it assumes I can run it. In a restoration mission, optimism is fatal. If I had followed the Tourist path, I would have started refactoring immediately—changing List to ArrayList< or adding Generics—likely breaking hidden behaviors I didn't fully understand. I would have been making blind changes to a fragile system without establishing its current state. To truly restore a brownfield project, I need to stop acting like Tourists and start acting like Archaeologists. Phase I: The Analysis After the “Tourist” prompt failed by offering a modern build that couldn't exist, I realized that what was needed here was not a tour guide, but a construction inspector. I shifted my mental model from “How do I run this?” to “Why did this fail?”. I reset the context with the AI, asking it to be critical rather than helpful. The Archaeologist Prompt I crafted a prompt designed to strip away the optimism. I assigned the AI a specific persona: Senior Legacy Systems Architect. I explicitly forbade it from summarizing the README which is often a lie in legacy projects and ordered a “Forensic Code Audit”. I am conducting a technical due diligence assessment on this legacy Java repository: https://github.com/nikmalykhin/java-blobstore. Act as a Senior Legacy Systems Architect. Your goal is not to tell me what the code "does," but to evaluate its structural health and "age." Do not summarize the README. Instead, perform a "Forensic Code Audit" focusing on these four pillars: 1. Carbon Dating The Era : Based on syntax e.g., raw types vs. generics, annotations , imports, and build tools Ant vs. Maven , estimate the specific Java version e.g., 1.4, 1.5, 6 and the year this code was likely written. Cite specific lines of code as "forensic evidence." 2. Architectural Integrity The Structure : Does it follow standard separation of concerns Transport vs. Protocol vs. Logic , or is it a "Big Ball of Mud"? Identify any "God Classes" that are doing too much. 3. Data Flow & Typing The "Stringly" Trap : Analyze how data is passed. Is it using proper Domain Objects, or is it relying on "Stringly-typed" Maps and raw arrays? Look for "Leaky Abstractions" where protocol details leak into business logic. 4. The "Safety" Check Error Handling & Threading : Look for anti-patterns in error handling swallowed exceptions, returning null . Analyze the threading model. Is SimpleBlobStoreImpl thread-safe? Output your findings as a structured "Risk Assessment Report" for a stakeholder deciding whether to refactor or rewrite. The AI dropped the polite facade immediately. Instead of a “Starter Kit,” it handed me a Risk Assessment Report with a brutal verdict: “ critical rewrite recommended “. Finding 1: Carbon Dating the Artifact The AI analyzed the syntax like an archaeologist uncovering historical strata, citing evidence rather than guessing. Looking first at the historical record, it found a build.xml file without any sign of a pom.xml , placing the project squarely in the pre-2010 “Ant Era.” Next, it flagged key syntax markers, spotting the legacy use of org.apache.commons.pool.ObjectPool Version 1.x alongside raw types like Map instead of Map