An AI skeptic developer's journey with AI

Or: how I kinda got to like it

By AngryDutchman (@angrydutchman.online)
Published:

Semi-disclaimer

So this will be one of those fun posts because the whole subject is polarizing and kind of spicy as all hell, so let me just say that when it comes to AI, I still believe that for things like drawing, painting, music, and the "true" creative pursuits people have, AI has no place there.

However...

What I do in daily life

My current job is one where I do a fair share of developing, not the user-facing stuff, but all the things you never see. And I develop tools for other developers at work to do their thing. So generally speaking I'm that creepy guy behind the curtain pulling all sorts of levers, and nobody's any the wiser. Also because nobody ever comes to visit me behind the curtain... anyway moving on...

So that's what I do. I've bounced between being an actual "pure" developer, system administrator, IT security consultant, and software architect over the past 30 years or so. (Yes, I'm old-ish :P).

Why I became an AI skeptic

Because I just am skeptic by nature. I refused to believe that AI would be of any benefit, considering the early versions of various models generally didn't generate code any better than a random intern pulled off the street. And a part of it required a bit of self-reflection: it scared the shit out of me. I'm at an age now where "keeping up" with developments is becoming harder, and while I can still write code, I'm not as fast at it as I used to be. Learning new things is also becoming harder, and that does add a fair bit of existential dread. And it could all just be between my ears, but hey, it was between my ears so...

The great experiment

My boss suggested I give it a shot, a while back, and I refused with my usual "I'm a skeptic and ..." bit. So then he dangled a free subscription to Claude with a budget for any additional usage every month. Free reign on what I want to use it for, and if I still don't like it after month 1, then fair enough.

I mean, I'm Dutch, we love free stuff, so I took him up on the offer, got my account sorted, subscription sorted, Claude Code installed... and then what?

The first steps

I write a lot of code in Perl. Yes, I'm an absolute dinosaur. I'm a fair hand at Python as well, and if pushed, I will admit to knowing enough Java to be dangerous. But I don't know any Go, for example. I mean, I can read Go code and infer it's meaning, I'm just not "up" on commonly used constructs and language idioms.

Anyway. So I figured I'd tell Claude to go look at a project I'd done in Perl. Nicely asked it to please summarize what it thought it was doing into a file that I could read later. Perl, on a good day, is reasonably readable. This particular project? It pulls some of the more filthy tricks for efficiency, and convenience. Like run-time class modifications, a fair bit of monkeypatching, and a lot of asynchronous work.

It took 5 minutes. 5 minutes in which I was sitting there like "alright, it'll fail, heck, it's Perl code. Only perl can parse Perl".

Imagine my surprise when out came a perfect summary of what it does, where functionality was contained, along with a note that it had found 2 potential bugs that I hadn't seen yet. Oh, well... shit.

The next step

I pondered a bit because I'd been toying with the idea of learning Go, and had started already, except I'd given in to my frustrations that Go doesn't let me do certain things that I'm used to being able to do, and goroutines and channels got confusing real damn fast for a bit. So I figured you know what, let me ask Claude. Could this project be ported to Go?

Yes.

Okay... let's go out on a limb because fuck it, I've got tokens for days, and I just outright told Claude to port my project from Perl to Go. I then went and did my shopping while Claude sat there spinning it's little wheels.

What it produced? Pretty clean. Pretty well organized. Functional parity with the Perl version. Except smaller. And with a quick benchmark also a fuckton faster at doing the job. Not surprising because Perl isn't exactly known to be a speed demon.

I spent the next week or so reading all the Go code, which got me pretty current with common patterns and the language in and of itself; made much easier because I could compare the Perl version (of which I know all the ins and outs) with the Go version.

Hum... so I guess AI can be a tool that helps you learn a new language. Because you can do stuff like this and can do a side-by-side comparison.

Further steps

I pulled up another Perl project. At work, we send all logs from all services, all servers, and every other thing not nailed down into a RabbitMQ cluster. The project? A processor that takes all these raw entries, and shoves them through configurable pipelines that munge data, look at data, and finally route it to a different topic based exchange in RabbitMQ for all our other tools to have easy access to only log entries they want, need, or have access to.

This means that not only are there pipeline definitions in JSON, there are comparators, operators, mutators, lookups, and of course a fair bit of glue to put this all together. The Perl version works very well, but has a realistic throughput of about 300-500 events per second. Considering we ingest, on average, 5000-6000 events per second with peaks up to 20.000 per second you can see why this particular thing burns up a fair chunk of budget, because it's just not fast/efficient enough.

So I did the same thing. Had Claude summarize it. Then told it to port the whole thing to Go. Took about 20 minutes, and I took a few days to see what it had done. And again, functional parity, no issues. I ran that first version alongside the Perl version in production, and it handled about 1000-1500 events per second. The biggest bottleneck was doing an external HTTP lookup that we had in place for legacy reasons, but this is where things really sort of started "working"...

The Eureka moment

Any developer should be familiar with the Rubber Ducky thing. The rubber ducky is, well, a rubber ducky. You talk to your rubber ducky about what you're doing, and by doing so, it should bring some more "aha" moments to your thoughts. The problem is, the rubber ducky doesn't talk back. Co-workers do, but that's got it's own issues when you work fully remote.

So... hm... you know Claude talks, in a way... maybe it can be my rubber ducky. I actually have a global config setting where I explain that it's job is to be exactly that. My rubber ducky. Minus the absolute creepy sycophant suck-up language it uses from time to time. Oh, and a rubber ducky that is allowed to write code.

Anyway. So having done that, I asked it to suggest ways to increase efficiency, considering it came from a single-thread synchronous Perl application. It suggested a few things. Again asked it to write it to a file so I could look at what it wanted to do. And it made perfect sense. So I went ahead and implemented it myself.

Then I asked Claude to look at it, and it decided there was nothing more it could do. Yay. I did a good! Deployed it, and now we're up to 2000 requests/sec handled.

But rubber ducky... so I started playing "what if I do X instead of Y". "How about dropping that external lookup and doing Z instead". Over the course of a day, I now had a clear idea of what was required to get things working even better.

And then I did something a lot of you will consider filthy. I asked Claude to handle the implementation. It spent another 20 minutes (and a fair chunk of my boss' money) doing it. This time I took a few hours to review what it had done, and signed off on it.

It's been running in production now for a few weeks. Instead of the 8-12 instances we needed to run of the Perl version (to handle peaks, because you don't want to wake up to a backlogged-by-2-million-events queue) we're now running 2 of the Go version.

Then I retired a couple of nodes in our workloads cluster because we didn't need them any longer.

So what came next?

More of the same, really. I've been letting Claude look at old Perl code, and porting it to Go. I also came to realize that, yeah, okay, I actually like thinking and designing things way more than I do writing code. I've always considered the code a tool that lets me get something done, and the something was always more important to me.

And this works surprisingly well, with AI assistance. I've been working on a side project for a while that I wrote an entire design document for. Then let Claude go over that, and played 20 questions-and-suggestions with it. I now have a document that defines something I've wanted to build for ages, and in some places surpasses my original idea on how it should be done.

I can hear people in the back screaming about vibe coding now...

And yes. Maybe it is vibe coding. I'm going to let Claude scaffold out the entire project, and if I see a part of it that I want to work on myself I will, but chances are I'll use Claude to generate a bunch of the code. Does it go into production right away? Well, no. Of course not, that'd be insanity.

As with previous projects, I feed Claude a description of the most common security vulnerabilities in web applications (path traversals, XSS, CSRF, SQL injections) and security practices (never, ever, ever trust a user to enter proper data, validate all the things, fail early and loudly, etc.) and ask it to follow all of that. And then I do an audit myself (I used to be an ISO27001 lead auditor) and so far? So far it's been actually doing a good job. But you do need to tell it to do that...

A few takeaways

Claude makes a good rubber duck. It also makes a good tool to make writing code (and boring things like documentation) easy. It does, however, need to be very explicitly told what it can and can't do - such as the security related things mentioned above. It also must be told explicitly how you are planning to scale a project (from the deployment end of things), how it's going to be deployed, and what other services you have available.

For instance in a previous for-work project it decided that it was going to implement it's own encrypted-at-rest key and certificate storage. Until I told it that, at work, we use HashiCorp Vault for secrets management. It then decided to implement it's own encrypted-at-rest key and certificate storage using Vault's transit encryption. It required a few back-and-forths to get it to just use Vault's key/value store (which is encrypted at rest) to do what it needed to do.

So I guess one takeaway is that you need to know what you're doing if you're going to have happy fun time with AI. I guess that can be summarized as "garbage in, garbage out".

There is also the problem, with Claude at least, that it seems to "forget" what has been done before, and it will end up hallucinating something, and it will absolutely double down on it. Or cases where you just end up having a vicious circle with it.

I recall having it make a change that instead of reading a file it could get it from Consul KV, and it implemented that in a wacky way , that I vetoed, and it went right back to "oh I'll just read it from file"; where you tell it no, you want to get it from Consul KV and it implements it the exact same way. Rinse, repeat, until you tell it to just stub it out and you end up doing your own implementation.

Final thoughts

Am I still a skeptic? Kind of. I've tried Claude, and while I think Claude does a pretty good job at taking away the more boring parts (writing code, documentation), and being a good rubber ducky, it's incredibly easy to fall into the trap where you trust it with everything. I'm paranoid, so my global prompt is about 2 pages long and consists mostly of things it shouldn't do at all, or should only do with explicit permission given.

I guess I'm no longer truly skeptical about AI's capabilities, I've now migrated my skepticism into the people side of things, where I'm skeptical that people can actualyl use AI in a way where it's not the AI driving, and you're just being a passenger.

You need to be in control. The AI is not the driver, you are. The AI is perhaps at worst a backseat driver, at best a passenger.

Final final thoughts.

Written at 6am after not enough sleep and not enough coffee, so any typo's or grammar mistakes I blame on that.

Also these are just my own experiences and thoughts, so take it with a grain of salt maybe.

And last but not least, I'm not going to engage in discourse about going to the dark side, or about how AI is the devil, or the question as to whether or not I'm vibe coding or making AI slop; after 30 years of writing code in a professional capacity I think I'm able to review things properly ;)