← Back to the story

The Journey

How a career in enterprise reporting turned into building AI infrastructure, one tool generation at a time.

I have been building reporting and data systems for most of my working life. The projects on this site all come from a single concentrated stretch of work, and read on their own they suggest someone who suddenly started shipping a great deal of software. That is not what happened, and the more useful story is the longer one: how each generation of tooling changed what was possible, which ones I adopted, which ones I abandoned, and why.

This page is that story. It exists because the individual project write-ups answer what and how but not how I got here, and the second question is the one that explains the first.

My work with AI tools began around July 2025, roughly a year before the most recent project described here. It did not begin with code, and it did not begin with anything I would have called a project at the time.

The foundation: two decades before any of this

Long before AI-assisted development, the work was enterprise reporting. Crystal Reports. SAP BusinessObjects. SQL Server Reporting Services. Stored procedures, semantic layers, scheduled distribution, and the accumulated knowledge of how a reporting estate actually behaves once it has been running for a decade and nobody remembers why a particular view exists.

That foundation is not incidental to what came later. It is the reason the later work is possible. When I look at a warehouse migration and immediately ask which reports resolve through synonyms and which bind to physical tables, that instinct is not something a tool provided. It came from years of being the person who had to fix it when the answer was wrong.

The database administration years

Reporting work never stays purely in reporting. Somebody has to own the databases underneath it, and for long stretches that was me: backups, restores, recovery testing, the unglamorous operational work that only becomes visible when it fails.

That work shaped how I think about recoverability more than any other part of my career. A backup nobody has restored is a hypothesis, not a safeguard, and the gap between those two only reveals itself at the worst possible moment. It is directly why a rule like never restoring a production database from another environment shows up in a project on this site decades later: the environments look interchangeable right up until the live configuration one of them holds turns out not to be reproducible.

The clearest current example is on this site as its own project. Blackbaud delivers our nightly database backups encrypted, and restoring one requires a certificate and matching private key installed inside SQL Server. When the vendor rotates that key pair, every restore breaks, and it breaks silently. Backups keep arriving on schedule. File sizes look correct. Monitoring stays green. The only signal is a decryption failure during an attempted restore, which by definition happens on a day when something has already gone wrong.

That work is written up as Encrypted Backup Key Rotation, and it is by a wide margin the smallest project described here. It is included because scope and consequence are not the same thing, and because operational work of this kind is invisible when done correctly, which makes it easy to leave off a portfolio and a mistake to do so.

First contact: five stages, not a switch

My engagement with AI-assisted development did not begin with agentic tools, and it did not begin with code. It began with Microsoft Copilot, the chat assistant that ships on Windows, and moved through five distinct stages, each of which taught something the next one built on: writing, then troubleshooting, then live production infrastructure, then a first attempt at building an application, and only then, once I knew exactly what was missing, choosing deliberately between tools.

That took about a year. The projects described on this site occupy only the last four months of it, and the eight months before them are the reason those four went the way they did.

That distinction matters for everything below. This was not an editor plugin suggesting the next line of code. It was a chat window. Every piece of code went in by pasting and came out by copying, which shaped both what the tool was useful for and how large the eventual gap to agentic tooling turned out to be.

Stage one: a communication tool

The first use cases were small and practical. Rewriting emails. Clarifying a technical explanation for a non-technical audience. Working out how to express an idea more precisely than I had managed on the first attempt.

That sounds trivial next to building software, and it was the necessary starting point. It is where I learned what the tool was actually good at and where it drifted, on low-stakes material where a bad output cost nothing but a second attempt. Judgment about a tool has to be built somewhere, and building it on production infrastructure is an expensive choice.

Stage two: troubleshooting

Use expanded into technical problems, and specifically into the kind that involve reading unfamiliar error messages across systems I did not own the internals of. Reporting platform failures. Mail library and transport errors. Endpoint security alerts that needed interpreting rather than merely acting on.

This is where I discovered the tool could do more than rewrite text. It could help reason through a problem, propose an explanation for an error, and suggest what to check next. Not authoritatively, and often wrongly, but as a way of generating hypotheses faster than working alone.

Stage three: real production infrastructure

The first use on something where a wrong answer would have had real consequences was the encrypted backup certificate work described above. That mattered as a threshold: the questions were specific, the documentation was ambiguous, and the cost of proceeding on a wrong assumption would have been discovering it during a restore.

I did not act on any of it without verifying independently, which is the discipline that made the threshold crossable at all.

Stage four: trying to build something

Around September 2025, roughly two months after first using the tool at all, I attempted an actual application. It turned out to be a much better teacher than a smooth success would have been.

The subject was the interface to our Remedyforce ticketing data. What existed was a Python application with a Tkinter desktop interface, launched from a command prompt on individual machines. It worked, and it had every property a long-lived internal tool accumulates: user interface logic, business logic, and data handling all in the same files, no defined application lifecycle, hard coded paths, and widgets reaching directly into internal state.

The moment that reframed the project came from trying to run it properly as a module rather than as a loose script. It failed immediately with an error saying the application object had no run method.

That error was not the problem. It was the symptom that exposed the problem. The application had no real entrypoint at all. One module assumed the existence of methods that had never been implemented, and nothing had ever caught that, because the code had only ever been executed one specific way. Years of implicit behavior had accumulated into something that worked exactly as long as nobody tried to run it differently.

Once that was visible, the list grew quickly: circular imports, callbacks welded to business logic, no separation between presentation and behavior, and modules that had never been designed to be imported by anything. This was not a codebase that needed tidying. It needed redesigning.

What a chat window could and could not do

Working through that refactor is where I learned the real shape of chat-based assistance for code, and the limitations were specific rather than vague.

It was genuinely good at reasoning. Describe a problem clearly and it would help think through the structure, explain why an error meant what it meant, and propose an approach. For diagnosing that entrypoint failure and working out what the architecture should become, it was a real contribution.

What it could not do was touch the project. Every constraint below follows from that single fact:

None of that made it useless. It made it a thinking partner with no hands. That is a meaningful thing to have, and it is a different category of tool from one that reads a repository, edits files, and runs a build.

Where that project went

The conclusion I drew was that the desktop interface was not worth salvaging, and I moved the whole thing to a web application built on FastAPI. Getting the first endpoint answering with a plain status message was a genuine milestone, because it confirmed the migration off Tkinter was real rather than theoretical.

From there it grew a proper structure, routes and services and models as separate concerns, Jinja2 templates, a browser interface replacing the desktop callbacks, and planning for hosting behind IIS with a reverse proxy. This ran on and off across several months rather than as a continuous effort, fitted around other work, which is itself part of the story: every gap meant re-establishing context that the tool did not retain.

It is also the direct ancestor of the Legacy Ticket Archive described on this site. The eventual production archive was rebuilt on our internal standard stack rather than continuing the FastAPI version, which is worth being straight about: the FastAPI work was the effort that proved the approach and mapped the problem, and the shipped system took the lessons rather than the code.

I do not regard that as a wasted project. Understanding exactly why the desktop tool had to go, and what a replacement actually needed to do, is what made the production rebuild fast when better tooling arrived.

Stage five: choosing a tool deliberately

Months of trying to build through a chat window is what made the next step obvious. I knew precisely what was missing, not from reading about agentic tooling but from having spent a long stretch personally acting as the bridge between a reasoning engine and a codebase.

So before starting anything substantial, we compared the options actually available: Microsoft Copilot, which by then I had been using for roughly eight months, Claude, and OpenAI’s tooling. The comparison was worth doing precisely because I could evaluate the candidates against a problem I had already failed to solve comfortably with one of them.

We selected Claude, and the timing lines up with the start of the first application built that way. That same discipline, comparison before adoption, is what later produced a formal evaluation of seven local models rather than picking one on reputation.

Choosing one tool for the build did not mean dropping the other. Through that first application, Copilot kept the role it had already proven good at, everything written for people rather than for a compiler: status communication to leadership, structuring requirements gathered from the programme team into stated rules, and rewriting technical explanations for non-technical readers. Claude Code did the implementation. Two tools, different jobs, running in parallel on the same project.

The transition that mattered

The shift from a chat window to agentic tooling changed the category of problem worth attempting, and the gap is larger than the phrase “AI assistance” makes it sound. In the first arrangement I was the hands: every file read, every change applied, every build run, every result reported back, all by me, manually, one paste at a time. In the second, the tool reads the codebase itself, proposes a plan, changes many files, runs the build, and responds to what broke.

The work I was doing did not change. What changed is that I stopped being the transport layer between the reasoning and the code.

The clearest measure of the difference is right here on this site. The Remedyforce interface rewrite ran on and off for months and reached deployment planning without shipping. The production archive that eventually replaced it was built, deployed, and running alongside eight other projects inside a single stretch of a few months.

An admission that reframes everything below

Before the project list, one thing has to be said plainly, because it changes how the rest should be read.

I worked as a web programmer roughly twenty years ago. Everything since has been reporting, data architecture, and database work. I can read code, reason about architecture, and review a design. Writing a modern web application by hand was not realistically available to me.

Without an agentic coding tool, most of the applications on this site do not get built. That is not self-deprecation, it is the actual precondition, and I think it makes the work more interesting rather than less. The question these projects answer is not how much faster a working developer becomes. It is whether someone with deep domain judgment and two-decade-old coding skills can deliver production software, and what has to be true about their process for that to be safe rather than reckless.

The first of them was taken on deliberately as a test of exactly that. Our main development team was committed to an HL7 integration with a county payer, and the residential programme’s need would otherwise have waited behind it.

How my working method changed

The method I use now looks nothing like the one I started with, and the shift happened gradually rather than by decision.

At first I worked like a programmer with an assistant. The tool wrote code, I reviewed it as carefully as I could, then I compiled, published, and tested by hand in Visual Studio. Every cycle passed through me at every stage, iterations were long, and much of each one was me verifying work I lacked the current expertise to verify quickly.

Then I moved up a level, once I could see where mistakes actually happened. The tool took over writing, compiling, building, and publishing. My line-by-line review had been catching very little that a compile and a real test would not catch faster, while consuming the time I should have spent on questions only I could answer.

What I kept was everything requiring judgment about the problem rather than the code. Testing against what staff actually do. Boundary conditions, where most real defects turned up. Working with analysts and project managers on what the application needed to do at all. Deciding something was wrong even when it worked.

Across these projects I have been the developer, the architect, the product manager, and the database administrator. Most of the implementation was done by the tool. Both statements are true at once, and stating them together is more useful than emphasising either alone.

Four phases across eighteen weeks

The projects on this site were not planned as a programme. Looking back at the order they happened in, though, a clear progression emerges, and it is one I would now recommend deliberately to anyone starting down this road.

PhaseProjects
1. Build real applicationsResidential Client Portal, Legacy Ticket Archive
2. Take on harder infrastructureSluice, and the BI platform migration plan
3. Systematize the practiceApplication Template, Job Scheduler, Shared AI Skills Library
4. Evaluate and govern the tooling itselfOn-Premises AI Evaluation, Medallion Hub

Eighteen weeks end to end, with the phases overlapping rather than running in strict sequence: the longest-running application was still being refined while the later phases were underway.

Phase one: prove it on real work

The first two projects were applications with real users and real consequences. The Residential Client Portal replaced spreadsheets and eleven separate lists as the system of record for a residential programme. The Legacy Ticket Archive kept a decade of retired support history searchable.

Neither was a pilot or a proof of concept, which was deliberate. A pilot proves a tool can produce code. Only real work with real users proves it can produce code you are willing to maintain.

Phase two: attempt things that were previously out of reach

Sluice is the project that changed my sense of what was possible. A parallel extraction platform with process isolation, heartbeat-based crash recovery, watchdogs for connections that freeze without erroring, and a bounded retry design, all built and hardened across twenty-eight working sessions. That is not a project I would have taken on solo with the tooling available two years earlier. Not because the concepts were beyond me, but because the volume of careful implementation work would have made the timeline indefensible.

Phase three: stop solving the same problem repeatedly

By the third phase, three applications had shipped and the same problems were being solved a third time, and worse, the same mistakes made a second time. That is what produced the Application Template and the Shared AI Skills Library: extracting the patterns and the working conventions into places where the next project inherits them rather than rediscovering them.

This phase is the one I would flag to anyone evaluating AI-assisted development seriously. Building fast is the easy part. The failure mode is building fast in nine slightly different ways, and the cure is deliberate consolidation before the divergence becomes permanent.

Phase four: turn the same scrutiny on the tools

The final phase went to two things: the largest architectural commitment in the set, a full data warehouse rebuild, and a structured evaluation of whether local language models could do useful work entirely inside our own infrastructure.

That evaluation matters beyond its result. Working with protected health information means the default answer to sending data outside the organization is no. A locally-hosted model changes that calculation, but only if it is genuinely capable, and the only way to establish that is measurement against real work. Seven models tested, two kept, and two of my own documented conclusions overturned by proper multi-sampling.

Applying the same evidentiary standard to the tools that I apply to the code is, I think, the part of this that generalizes furthest.

What the arc actually shows

Four things, if I had to name them for someone deciding whether this experience transfers.

Adoption was gradual and staged, not a leap. Communication, then troubleshooting, then production infrastructure, then tool selection, then building. Eight months passed between first use and the first application built this way. Each stage established enough judgment about the tool to justify the next, and the sequence matters more than the endpoint: I was using these tools on low-stakes work well before most teams around me had tried them at all, which is how I had a considered opinion by the time the stakes rose.

Adoption tied to constraint, not novelty. Each tooling change was taken up because it removed a specific limitation, not because it was new. The corresponding discipline is that things get dropped too: five language models deleted after measurement, a fully diagnosed authentication fix deliberately not applied because the component was being retired anyway.

Depth in the domain came first. The architectural judgment in these projects, knowing that a reporting estate resolves through an indirection layer that can be repointed, knowing that a silent wrong number is worse than a loud failure, comes from the reporting years rather than from any tool.

The practice got engineered, not just the software. The methodology, the shared instruction library, the documented failure modes, the multi-sampling rule. In a regulated environment, being able to show how AI-assisted work is governed is part of the job, and it is not answerable by pointing at good outcomes.

The bottleneck moved from capability to adoption. Once the tooling was proven and the patterns were captured, the limiting factor stopped being whether the work could be done and became whether a team would take it up. That is a people problem rather than an engineering one, it is the problem I have made least progress on, and it is now the one I spend the most time thinking about.

Delivery is not the same as development. Getting an application to the point where an operational team fully relies on it takes roughly a year in an organisation this size, and the Residential Client Portal is partway through that. Not because the code takes a year, but because a small team has long lead times for anything entering production, and those are almost entirely independent of how the code gets written. Review cycles, security approval, coordinating people whose main job is something else, testing against real workflows, training, and waiting for a safe moment in an operational calendar. None of that got faster. I would be suspicious of anyone claiming otherwise, and I would rather say so here than have someone discover the caveat after taking my word for the rest.

Mistakes are recorded, including my own published ones. Every project here keeps a running lessons file. Two conclusions in the model evaluation were wrong and are corrected in place rather than quietly replaced. That is not self-flagellation; it is the mechanism that stops a mistake from recurring, and it is why the failure catalogue on this site is as detailed as the successes.

What the work turned into

Something changed about the nature of these projects partway through, and I did not plan it.

The first few were applications. Somebody needed a system, and I built it. By the third one the interesting problems had moved: the same mistakes were being made twice in different codebases, the same setup problems solved from scratch, the same conventions re-derived. So the next things I built were not applications at all. A shared template so a project starts from established patterns rather than an empty folder. A shared instruction library so working conventions live in version control instead of individual habits. A scheduler so recurring jobs stop living inside whichever application happened to need one first.

None of those have users in the ordinary sense. Their entire purpose is that the next person, or the next project, does not repeat something. That is a different kind of work from building an application, and it is the kind I have found myself drawn to.

There is an old line about the shoemaker’s children going barefoot, and it describes technology teams precisely. Everything we build goes to the business, because that is where the visible need and the funding are. The small tool that would make our own work easier never reaches the top of the list, not through neglect but because it cannot be justified against work somebody is waiting for.

For most of my career that meant those tools went unbuilt. The change I did not anticipate is that AI assistance altered the economics rather than only the speed: a diagnostic tool that would have taken three days, and therefore never happened, became an afternoon. A whole category of permanently deferred work became affordable, and much of what I have built since is in that category.

The same shift shows up in the decisions rather than the deliverables. Choosing the smaller architecture because a small team has to maintain it. Rejecting a hardware purchase because routing had already solved the problem at no cost. Documenting a diagnosis and then deliberately not applying it, because the component was being retired and the hours were better spent elsewhere. Deferring two security findings with a written reason rather than rushing them next to a production push. Writing down that a scheduler must never host a four-hour job, so that nobody discovers why the hard way.

Each of those is a judgment about where finite attention should go, made on behalf of a team rather than a codebase. Reading them back together, they describe the job I am actually doing more accurately than any of the individual projects do.

Where this is going

The direction I am working toward is ownership of how an organisation adopts AI capability, rather than the use of it. That means the governance framework as much as the implementation: which work stays entirely on infrastructure we control, which can use a stronger external model under a defined agreement, and which should not involve AI assistance at all regardless of what is available.

The projects are the evidence that the technical side is real. The methodology, the shared instruction library, the measured model evaluation, and the documented failure modes are the evidence that the governance side is too.

The honest gap is described in the part I have not solved: I have demonstrated the practice and I have not yet made it normal across a team. A department where this work is routine, governed, and reviewable is a different achievement from one person delivering ten projects, and it is the one I am now most interested in.