Several years ago, I started experimenting with controlling a model railway through a Roco Z21 command station.
Looking back, I was really trying to solve three problems at once.
I wanted to show that coding could be easy and fun by using it to control something physical. I wanted to make a complicated German-language protocol specification more accessible to an international audience by explaining it in English. And I wanted to learn Python and refresh my own coding skills.
That meant implementing the Z21 protocol was not just scaffolding.
It was part of the purpose of the exercise.
My first experiment was simple: retrieve the Z21 serial number. But even that meant understanding the protocol, byte ordering and UDP communication, then translating all of it into Python.
As the project grew, so did the scaffolding. Incoming packets could contain multiple messages, so I needed to split them. Different messages needed dispatching. Some information arrived asynchronously. Feedback modules reported track occupancy. Locomotive control introduced another protocol carried through the Z21 protocol.
Along the way I also started experimenting with functional programming, refactoring, behavioral examples and automated tests.
All of that was useful. But I was simultaneously learning the protocol, learning Python, designing the software, debugging it, documenting it and trying to automate a railway.
Returning to the same problem
Several years later, I came back to the project.
The railway has not fundamentally changed. Neither has the protocol.
The development environment has.
Today I can give AI the Z21 protocol specification and ask how to retrieve the serial number. It can find the relevant section, interpret it, explain the request and response and produce a candidate implementation.
But code generation is only a small part of what changed.
I could also give AI my old implementations. It found bugs in code I had written, compared my interpretation with the specification, suggested improvements and helped reconstruct what I had been trying to do.
Where I had previously become stuck, it could suggest possible ways forward.
When I wanted to develop without depending on the physical command station, it helped design a small Z21 simulator over UDP on localhost.
When I wanted to return to automated testing, it could help translate acceptance criteria into Python unittest tests.
And when an answer looked questionable, I could challenge it.
That matters. AI was not always right. I corrected its reconstruction of the history of some of my old experiments, and I questioned its interpretation of parts of the protocol.
The point is not that AI provides trustworthy answers automatically.
The point is that it removes a great deal of friction.
Three problems that largely disappeared
The contrast with the original project is striking.
Previously, I wanted to make coding accessible, translate a German specification for an international audience and learn Python while refreshing my programming skills.
Today, none of those needs to be a prerequisite for the real experiment.
Roco published the specification finally in English. AI can help produce and inspect Python. If I encounter a language feature or protocol detail I do not understand, I can investigate it at the moment it becomes relevant.
Technical knowledge has not disappeared. AI can supply implementation proposals on demand, but my understanding of what good software looks like becomes what allows me to direct, challenge and improve those proposals.
But much of it has moved from being a prerequisite to being an on-demand resource.
That changes where I can spend my attention.
Before, the path often looked like this:
intent → documentation → technical understanding → implementation → debugging → experiment
Now it can look more like this:
intent → user story → acceptance criteria → automated tests → implementation → experiment → observation and learning
All AI assisted. The implementation is still there. So is the technical knowledge behind it. But they no longer need to dominate the work.
That leaves more room for the questions I actually care about:
What am I trying to achieve?
What behavior do I require?
How will I know that it works?
What assumptions am I making?
What did I learn from the result?
Not just programming
I recognize the same pattern when working with AI on Practice Notes and other documents.
I bring intent, observations and source material. AI helps analyze, retrieve, compare, synthesize and draft. I inspect the result, challenge it, provide more evidence and iterate.
The artifact may be prose instead of Python, but the pattern is similar:
intent → AI-assisted interpretation → AI-generated candidate artifact → judgment → evidence → revision
For software, some of that evidence can be executable. User stories preserve intent. Acceptance criteria describe expected behavior. Automated tests make those expectations executable. A simulator provides a controlled test environment. Eventually, the physical model railway layout provides another form of evidence.
AI can help throughout that chain without becoming the authority on whether the result is correct.
From implementation to intent
The first time around, implementing the Z21 protocol was part of the exploration.
I wanted to understand it, explain it and learn how to implement it.
This time, I do not particularly need to rediscover how to implement a binary railway protocol.
I want to explore what I can do with it.
Earlier, I explored how to implement the Z21 protocol.
This time, I can explore what I want to do with it.
And perhaps that is the broader lesson:
AI does not merely make implementation faster. It can move implementation knowledge from a prerequisite toward an on-demand resource, allowing more attention to move toward intent, evidence, judgment and learning.
Leave a Reply