This article was originally written in Japanese and translated into English with AI assistance. Please note that some expressions may carry nuances from the original Japanese.
🇯🇵 日本語版はこちら / Japanese version
Series: DataMigrator Development Journal (Part 6)
Last time, I progressed up to introducing Claude Code and preparing the “box” of the DataMigrator project. However, at this stage so far, not a single line of code has been written. All that’s done is an empty house — the furniture and electricity inside are still to come.
This time is about putting the first code into that empty box. It’s a record of the session where I implemented M1 (the project foundation), the first of the milestones I’d marked off in the design phase. To get to the point: it finished in about two hours. How far did someone with zero programming experience get in two hours? And it’s the story of how, in this session, the real feeling of “I’m building software” sprouted for the first time.
M1’s Goals
What I’d do in the M1 milestone was, roughly, the following four things.
The first is to set up the Python development environment. The second is to actually create, as files, the directory structure written in the design document (eight subpackages under src, a tests directory, a resources directory, and so on). The third is to implement a class called “DBManager” that handles the database. The fourth is to set up a mechanism (CI/CD) where automated tests run on GitHub.
All of these are things already written in detail in the design documents. In other words, it comes down to just realizing, as actual files, what was written in the design documents. What I, the human, do is, in its basic form, give Claude Code the instruction “I’m starting the M1 implementation. Refer to the design documents and do it in order,” and then watch over the screen and keep pressing the Allow button.
uv Was Fast, Just as the Rumors Said
The first thing I did was build the Python environment. Here, for the first time, I used a tool called “uv.”
The procedure manual I originally had (the MacBook Pro dev-environment setup manual) called for building the Python environment with a combination of pyenv and pip. However, at this M1 timing, I decided to switch to a new Rust-made tool called uv. The reason is simple: at the planning stage of the blog series, I’d heard that “uv is faster and simpler.” The roles that had been split in two between pyenv and pip (Python version management and package management), uv combines into one.
Claude Code created a virtual environment with uv venv and installed the dependencies all at once with uv pip install -e ".[dev]". The installation of about twenty packages — including a heavyweight like PySide6 alone at 307MB — took, by feel, about a minute. The sheer speed made me chuckle a little. Claude Code explained, “it’s like the Python version of Cargo,” and I was impressed — ah, so that analogy holds (Cargo, apparently, is the package manager of the programming language Rust).
Incidentally, uv itself had apparently already been installed by Homebrew on its own, and it had become usable without my knowing. I think it got in as some dependency. The “newly install uv” step written in the procedure manual was passed over almost entirely.
The Directory Skeleton — 78 Files Born at Once
Next, I create the directory structure exactly as written in Design Document 02 (the source-code organization design). I had Claude Code refer to the design document and instructed it, “please lay out the empty files exactly as written in §3 of the design document.”
A few seconds later, eight subpackages (gui, controllers, engines, data, models, workers, config, utils) were created under src/datamigrator, each lined with an empty __init__.py and the main empty files. The tests directory, too, was lined with the frames of test files. Seventy-eight files in total. The contents only said “# placeholder,” but looking at the directory tree, the structure was indeed there, just as in the design document.
At this moment, I had a sensation of, how to put it, just the framework of a model kit having been assembled. It doesn’t move yet, but the form is there.
And here, right away, there was a small stumble. When I tried running import datamigrator as a test, an error came out. The cause was that the file pyproject.toml was missing the package-detection setting. When you take a placement method called the “src layout,” you apparently have to explicitly tell setuptools to “look inside the src folder,” but that setting was missing.
If it were me, the human, I’d surely spend about thirty minutes here investigating “why can’t I import it.” But Claude Code read the error message itself, self-diagnosed the cause, added a [tool.setuptools.packages.find] section to pyproject.toml, reinstalled, and checked that it worked again. All of it proceeds automatically before my eyes. I was just pressing “Yes” and “Yes” and “Yes.” Imagining the future where I’d investigate at each error, I felt a little detached from reality.
The AI Wrote Tests Without Being Told To
There’s a moment in this M1 session that surprised me the most. It was after implementing the class called DBManager.
I asked Claude Code as follows: “Refer to Design Document 05 (the DB schema) and implement the DBManager class in src/datamigrator/data/db_manager.py. Please include enabling WAL mode, table creation, and migration management.” A simple request.
Claude Code read the design document, implemented the DBManager class, and saved the file. So far, as expected. But in the next turn, Claude Code spontaneously began creating a test file called tests/test_db_manager.py. It was about 228 lines of test code. I hadn’t said a single word about wanting tests written.
What’s more, the contents were quite serious. “Is WAL mode enabled,” “does migration work correctly,” “does exclusive locking function,” “does trying to write on a read-only connection produce an error,” “are the table constraints properly in effect” — and so on, it covered just about all the problems that could occur around the DB. When I ran the tests, all sixteen passed. Coverage was 84%. Without quite understanding what the numbers meant, the mere vibe of “apparently these are good numbers” came across.
The moment I saw this, I was momentarily confused — “wait, what’s my position here, again?” What I, the human who’d given the instruction, had envisioned was only “implementation,” and I’d intended to ask for “tests” separately — yet Claude Code had read that and gotten ahead of me.
When I later consulted the Claude.ai side about “why did this happen,” it came back with: “It’s likely that the project’s CLAUDE.md had a policy written in it that ‘after implementation, write tests,’ or that it referred to the test-design document (09) and judged autonomously.” It was also the moment it sank in: “ah, so the effect of the CLAUDE.md I wrote last time shows up in a form like this.”
As an aside, when running these tests, one warning came out from ruff (a code-formatting tool) that the import order was off. It’s a trivial matter, but Claude Code auto-fixed it with the --fix option, rechecked, and carried it all the way to “All checks passed.” Here, too, I did nothing.
A GitHub Permission Error, and the Separate-Terminal Problem
Finally, there was just one stumble in setting up CI/CD.
CI/CD is a mechanism that “automatically runs tests every time you push code to GitHub.” I set this up with two YAML-format files test.yml and build.yml), and when I tried to git push as usual, I was rejected with an error. The message was to the effect that “since the OAuth token doesn’t have the workflow scope, files under .github/workflows can’t be accepted.”
Understanding only half of what it meant, I consulted Claude Code, and it told me: “Running gh auth refresh -s workflow -h github.com will resolve it. However, this requires interactive authentication, so it can’t be run inside Claude Code. Please run it in a separate terminal.”
There’s a constraint, apparently, that launching interactive browser authentication from inside Claude Code is difficult. As told, I opened a separate terminal, ran the instructed command, added the workflow scope in the browser, came back, retried — and the push went through fine.
This “interactive authentication in a separate terminal” pattern is something I’d encounter once more later, over on the utf8conv side. Once you stumble, the second time you can deal with it instantly using the same procedure. It’s the sensation of a little bit of know-how piling up.
After M1 Ended, What Remained
At the end of the session, I counted up what had been made in M1: five commits, 78 files created, four files of implementation code, two CI/CD files, sixteen tests (all passing), 84% coverage, six DB tables with twenty indexes. Working time, about two hours.
I myself wrote almost nothing. I handed over the design documents, gave Claude Code instructions, pressed Yes on the confirmation dialogs, and occasionally asked “what does this mean?” — that’s all. And yet, inside the screen, a “working database layer” had been built, the tests run, and they run in CI too.
At M1’s end, I organized the blog-material journal together in the chat on the Claude.ai side. There, I left a one-line note as my own impression: “if the design documents are properly in order, you can entrust the implementation this far.”
There was one more impression that remained: “maybe it’s become an age where, even with zero programming experience, you can develop as long as you have design skills.” That said, I immediately want to attach a caveat to this impression myself. I borrow Claude’s power even to write the design documents, and to supervise the implementation I have to understand at least the minimum of technical terms. It’s by no means a case of “you don’t have to do anything.” To put it accurately, an expression like “the possibility has emerged of getting to be on the side that builds software, even without writing the code yourself” is probably closer to the reality.
Still, the fact that the empty box turned, in two hours, into “a box with a working database layer in it” was, as a fact, a happy event. You might call it the night when someone who’d done theater as an enthusiastic amateur first touched their own software.
Next Time
Next time, I’ll write about the developments after M1, where DataMigrator as a whole currently stands, and the outlook toward release. To return to my main business of AI development, do I need to complete DataMigrator, or is it okay to wrap it up partway? And I also plan to touch a little on its relationship with another small product progressing in parallel, “utf8conv.” I’ll treat that as a stopping point for the DataMigrator series for now, and connect it to the next series (the utf8conv series).
About Soul Resonant Works
Soul Resonant Works is a solo venture developing seven local AI systems. Starting from zero programming experience, the development is progressing through collaboration with AI.
🌐 Soul Resonant Works:
→ https://sr-works.net/en/index.html
📝 This blog publishes the entire development process as a serialized journal.
If you found this article useful, please share it.
Leave a Reply