AINetwork automationpyATSNetClawVibeOps

Life Before AI: The Merlin Years

Looking at my Merlin repositories now feels a little like finding a rotary phone at a garage sale.

I know how to use it. I remember the effort. There is something wonderful about the mechanism, the patience, the fact that we made the whole thing work.

I would also very much like to keep my iPhone.

Across four public Merlin repositories, the codebase adds up to 86,688 non-blank lines of hand-written code and documentation. About 84,000 of those lines are mine. The work represents an estimated 550 hours, with 293 of my commits across 113 recorded days of activity. More than one in four commits happened on a weekend.

And across those four repositories? Zero unit test files. Zero CI pipelines.

That is my baseline for life before AI coding agents. I was the network engineer, the developer, the person reading the documentation, the person typing the closing tags, and the person deciding that it worked because I had just watched it run.

I am proud of Merlin. It also helped give me a head start when AI arrived. Both of those things can be true while I give myself permission to build very differently today.

What Merlin actually was

The idea was network magic: take the CLI and network APIs, collect structured state with tools such as pyATS and Genie, and turn it into something people could use. Reports. Tables. Diagrams. Searchable information. Eventually a web application, chat integrations, containers, and even 3D worlds.

If you have followed Automate Your Network, my pyATS work, or the more recent NetClaw experiments, you will recognize the ambition. I have spent years trying to make the information inside a network easier to reach and understand.

Merlin was an early, very hands-on expression of that ambition.

Repository What I was building Hand-written lines
merlin Network data collection and templated reports 33,022
merlin_unchained A Django web application around that work 45,765
merlin3d Network state brought into Blender 825
merlin_docker_nexus A containerized Nexus sandbox implementation 7,076
Total 86,688

The repository histories span May 2021 through March 2023. The line count excludes generated sample output and third-party libraries. It includes comments and documentation, and it includes the work of contributors, who deserve their credit. My share is about 97%.

At fifty lines per printed page, that is roughly 1,700 pages. The retained hand-written files contain about 4.9 million characters. That is a measure of the code sitting in the repositories, not a recording of every keystroke; it cannot show how often I deleted something and tried again.

The biggest categories were 32,948 lines of Django HTML templates, 24,544 lines of Python, and 24,502 lines of Jinja2.

I can still feel the brackets.

I had to learn Django. DJANGO.

I am a network engineer.

But I wanted a browser interface for Merlin. I wanted someone to click a button, run a job, look up a device, search the results, and download a report. So I learned Django.

The ORM. Database models. Migrations. URL routing. Views. Template inheritance. Static files. Packaging the whole thing so somebody else could run it.

The resulting merlin_unchained application contained 12 Django apps, 409 view functions and classes, 401 URL routes, 36 database models, and 249 HTML templates. Those HTML templates held 5,754 template-variable expressions. Another 67 standalone pyATS job scripts fed the application.

Every route needed to reach the right view. Every view needed the right data. Every template needed to know what to do with it.

Then came Elasticsearch, Kibana, Prometheus, Grafana, Twilio voice and SMS, Webex, and AWS S3. Each integration brought another set of documentation and another set of assumptions to understand.

One commit message from October 27, 2021, at 7:42 p.m. reads:

working - just need to finish the rest of the commands now

That captures the era beautifully. I had figured out the pattern. Now I had to personally repeat the pattern until the application existed.

Django alone accounts for about 311 estimated hours of the work.

One closing tag at a time

Across the four repositories, there are 379 Jinja2 templates, containing about 1.9 million characters.

Inside those templates: 1,865 for loops, 3,040 if conditions, and 4,905 corresponding endfor and endif tags. The opening and closing counts balance in the files that survived. Getting there was my job.

There are also 14,348 variable expressions. Each one needed the right path into the data returned by a parser or API. Knowing the command was only the beginning. I also had to know the shape of its output and how to reach the value I wanted.

The templates multiplied across platforms, commands, and output formats. In the original Merlin repository, 134 templates exist just to draw NetJSON network graphs.

Yes, I copied and pasted. Yes, I reused patterns. But I still had to make each copy correct for the next command, the next platform, the next report. I held the syntax in my head and edited it character by character.

The rotary phone comparison fits because the effort was built into the interface. You could know exactly whom you wanted to call and still have to turn the wheel for every digit.

I knew what I wanted the report to say. I still had to type the tags.

The evenings are in the git history

The best estimate is about 550 hours of hands-on work, roughly fourteen forty-hour weeks. It is an estimate from commit sessions, with an adjustment for code written before the first commit; the plausible range is broad, about 380 to 1,000 hours.

Even that method cannot see the research, documentation, sandbox experiments, or live coding that never ended in a commit.

26% of my commits happened on weekends. Another way of looking at the timestamps: 28% landed between 6 p.m. and 6 a.m. Those groups overlap; they are two views of the same working life.

On Sunday, October 17, 2021, I committed eight times, from 11:14 in the morning until 10:17 at night. That does not prove I worked continuously for eleven hours. It does tell you how far into my Sunday this project reached.

I enjoyed building it. I learned an enormous amount. I shared the work on YouTube because I wanted other network engineers to see that they could build these things too.

But when somebody tells me that writing every line yourself is the proper way to do this, I have a fairly specific answer. I have done it. I have the repositories, the templates, and the Sunday-night commits.

I do not need to repeat that experience to prove I am an engineer.

The detour gave me a head start

Here is the part I do not want to lose in a story about how much easier building has become: learning Django mattered.

When the opportunity came to build the NetworkGPT plugin for Cisco, I wrote it in two weeks. The Django experience from Merlin helped make that possible. I already knew how to put a web application around network data, connect the pieces, and get something working that I could demonstrate.

You can see where that led in my NetworkGPT presentation at Cisco Live 2023.

NetworkGPT at Cisco Live 2023. The Django work behind Merlin helped me move quickly when the next opportunity arrived.

Would I have had the same head start with AI without those Merlin years? I honestly do not know. It is hard to separate the opportunity from the preparation.

I knew what network data looked like. I knew what an API had to expose. I had already wrestled with taking something useful in a terminal and making it useful to somebody else. When AI gave me a new interface, there was a lot of hard-earned experience underneath it.

That progression continued through pyATS MCP, ISE MCP, ACI MCP, and the work that became NetClaw. The tools changed; years of networking experience kept informing the questions I asked them.

Some of the callbacks are almost funny. In 2021, Merlin was talking through Webex, and I was putting network state into Blender. Years later, NetClaw gained Webex conversations and Blender visualization, followed by a real Cisco topology brought into Unreal Engine.

Apparently I still want to talk to my network and walk around inside it. I have much better ways to get there now.

I can value what the old process taught me without making it a prerequisite for everyone who comes after me. Your experience might give you a different head start. You are allowed to begin from where you are.

I might not even know what language it is in

During Merlin, I was juggling ten languages and syntaxes: Python, Jinja2, Django templates, YAML, HTML, CSS, JavaScript, Dockerfile syntax, shell, and Elasticsearch queries.

Today, there are projects where I climb this whole ladder:

  1. I am not writing the code anymore.
  2. I am not even reading the code anymore.
  3. Sometimes I do not even know what language it is in.

And for a lot of the things I want to build, I should not have to care.

If an agent can choose an appropriate implementation, meet the constraints, and demonstrate that the result works, memorizing another language does not have to be the price of admission. I can ask about the choice when it affects deployment, maintenance, performance, or the people who will inherit the system.

I still care deeply about what the software does. I care where the data goes, what the system can change, how it fails, and what evidence supports its answer. Those are questions I can bring to the work without personally reading every loop.

For a network engineer, this is familiar territory. We have spent careers operating complex systems whose complete source code we have never read. We build confidence through understood behavior, observations, constraints, and testing.

“Tested and working” deserves a closer look

My Merlin commit messages include “tested and working,” “elastic tested working,” and “webex tested.” On that October Sunday, one message at 9:39 p.m. says:

fixes all tested and working all

I was testing against networks and sandboxes. That work mattered. But the repository audit found no unit test files and no CI pipelines. The evidence was a manual run I had performed, without a repeatable automated suite to check the next change.

There is an irony here: I was using pyATS, a network test framework, while leaving my own surrounding application without unit tests.

Writing the code myself did not automatically give me a better verification process.

The change I want from agents includes the verification work: define the expected behavior, build the checks, run them, investigate the failures, and keep the evidence. An agent saying “done” is a claim to verify.

My twenty-three specs in six days account gives a concrete example. In that NetClaw development run, checks of declarations and configuration were passing while seven registered servers could not actually start. Launching the processes exposed the problem. Live device checks found other errors that the earlier checks had missed.

That is a useful improvement over my Sunday-night commit message: a check that can expose a failure again, and a reason to expand what the checks cover.

It also keeps me honest. A green test suite proves only what it exercises. A test against a local fixture, a sandbox run, and an observation from a real device answer different questions. I want those differences visible.

Give yourself permission

This is what I want another engineer to take away from the Merlin years:

  • Give yourself permission to vibe code. Explore an idea. Make a game. Build a tool you wish existed. Let the first version teach you what you actually want.
  • Give yourself permission to use spec-driven development. Write down the behavior, constraints, and acceptance criteria. Give the agent something concrete to implement and prove.
  • Give yourself permission to NOT write your code. You can contribute the problem, the design, the domain knowledge, and the judgment.
  • Give yourself permission to NOT read your code. You can choose where review helps and require evidence that the result meets your expectations.

You can move between those approaches. An experiment can become a specification. A specification can reveal a question you need to explore. You can read a difficult piece of code because it interests you or helps solve a problem, without turning line-by-line reading into a ritual you owe everybody else.

The responsibility for the outcome stays with you. The keyboard work can move.

This is part of why VibeOps matters to me. I want engineers to have room to try things, share what happened, and improve their methods. There is so much useful experience in this community. We should be helping people bring that experience into what they build.

What I wanted to do The Merlin years What I can ask for now
Produce a report Write the parser access and every template Describe the report and verify it against the source data
Add a web interface Learn Django and wire hundreds of routes Specify the user journey and test it in a browser
Draw the network Build and maintain another set of templates Ask for a visualization grounded in observed topology
Check a change Run it manually and record that it worked Require repeatable checks and inspect the results

The rotary phone got the call through. It was an important step. So was Merlin.

Those years helped me become the person who could build NetworkGPT in two weeks and recognize where the next wave of tools might take network engineering. I am grateful for that preparation.

I am also grateful that the next idea does not have to cost another 4,905 closing tags.

Give yourself permission to build it.

About the numbers

The figures in this article come from an audit of the four public Merlin repositories linked above. Lines are non-blank lines at the audited repository heads, including comments and documentation. About 166,000 lines of generated output and about 4,800 lines of third-party libraries were excluded. The private merlin_kubernetes repository is outside the scope.

Git blame attributes about 84,000 lines to me, with about 2,460 from xaprodev and smaller contributions from Danny Wade, Oren Brigg, and others. About 2,500 lines are exact copies reused across repositories, bringing the unique total to roughly 84,000. The authorship estimate and the deduplicated total happen to round to the same number; they measure different things.

The hours estimate groups my non-merge commits into sessions separated by gaps of less than two hours, allowing two hours before the first commit in each session. That produces 173 sessions and roughly 420 hours of visible activity. The first Merlin commit on May 16, 2021, introduced 25,022 lines at once. An allowance of about 135 hours covers the 20,231 hand-written lines surviving from that commit, using an estimated 150 lines per hour. Rounded together, that is about 550 hours. Different session rules and a slower assumed pace for the pre-repository work produce the roughly 380–1,000-hour range. This is a reconstruction, not a timesheet.

The test search found no unit test files, no tests.py containing test functions, and no CI workflows in these repositories. The file called ping_test.py performs a network ping check. It is not a unit test of the application. These findings describe what the repositories preserve; they do not claim that no manual testing ever happened.

← More field notes