Denial.
My first experience with AI coding assistants did not get me off to the best start or give me great reason for optimism. At the time, I was working for a retail bank and due to the obvious security and compliance concerns, their AI agent - called Spirare - was somewhat hobbled, to put it mildly. In fairness, it most likely couldn't call third party servers or send any data off-site as part of the software providers SLA with the bank and that badly degraded its performance. You'd give it a task and most of the time it would turn your laptop into a grill and fail to respond at all and eventually timeout. This was literally my first experience with AI coding agents and it started me off with a deep scepticism about how Agentic Coding Assistants will impact development.
I did finally start to get some use out of it, first by writing tests. The pre-existing human-produced code was already very well tested, all functionality was covered as part of our teams policy (enforced at CI level) to ensure all code was tested. But around late 2024, commandments were delivered in stone from up-on-high - a board-pleasing C-suite decision was taken to "improve" our test coverage by going from ~87% coverage to 100%. No amount of pleading and convincing from us developers that this was a misguided goal was sufficient so we had to ensure every line of this aging monolith had test coverage - potentially a huge drain on our developers, who were already overworked: a complete waste of time and resources.
So we decided to employ this new AI assistant to help with that and to be fair, without diverting any of our resources (humans) from their usual responsibilities we managed to nudge the test coverage up to > 95%. The test suite drove the amount of time it takes a PR to go through the aging CI infra through the roof (*clicks push*, "Coffee?") but it got us a lot closer to our goal without impacting our velocity. The tests were garbage and it often needed manual adjustments to get them to work but the goal of this was not to test functionality it was to ensure every line (even the redundant legacy code) was covered to satisfy this coveted 100% coverage goal.
Anger.
This all happened just as the AI hype machine was starting to enter the irrational exuberance stage: Microsoft, Meta, Oracle, "the other place" and a dozen other tech companies were all laying-off developers in the thousands and instead of paying wages, they started throwing their former-employees salaries into the compute data-center money pit. Ever-increasing, dizzying amounts of money were being pledged to build air-polluting, river-draining, globe-warming AI data centres to keep the hype train rolling and it was starting to look very much like a tech bubble to end all tech bubbles.
At the center of it all was Nvidia signing multi-billion dollar agreements with these firms to provide the chips that will be needed in these data centers, the price of RAM was doubling and doubling again. God help you if you wanted to build a new PC in this environment - it would cost twice as much as it would have done a year ago for the same spec. It really felt like the tech companies were just passing money between each other to inflate their valuations and at the same time, making thousands redundant and feeding the public truck-loads of bullshit. The promises they were making were so detached from the reality I was seeing that it put my in a very cynical frame of mind. At this point I was sure, AI is just one-big scam to syphon off as much cash-money as possible before the next market crash.
Bargaining.
But while all this was going on I was seeing something that gave me pause. People like Ed Zitron were making very compelling, data-supported arguments about how the whole LLM/AI sandcastle was built on a motte of bullshit and the whole thing will come crashing down, but in contrast to that there were developers and educators that were going all in AI. People like Kent C Dodds and Matt Pocock - both developers who I have the greatest respect and admiration for - were talking about the changes AI were making to the way we work in the most glowing terms. These are highly respected, intelligent people - some of the brightest minds in developer advocacy. Surely they are not just jumping on the AI hype train without good cause, maybe they know something that perhaps I don't?
Depression.
At around this time I found myself in the same boat as many of my peers and my contract was not renewed. I don't think AI was to blame for this to be honest: In Singapore, where I live, the government had recently changed the rules on the qualifying salary for Employment Passes - of which I was dependent on for my right to reside. The salary increase I needed to qualify was not within the budget of my employer so they had to let me go.
On the one hand, I can understand why my employer would decide to let me go - thanks to the ministry of manpower's regulations they can probably get a migrant worker to do the job I was doing for them for less than half of what they'd have to pay me due to my age and experience under these new rules. On the other hand it is extremely disruptive to my life and with all the AI-related layoffs, I found myself in a position I had not been in for about 15 years - unemployed and in a over-saturated highly-competitive jobs market. Average salaries for people in our sector were falling at the same time as the new ruled dictated that the amount I have to ask for to qualify for an employment pass application had risen drastically - I had been priced out of the market dramatically reducing the number of companies that could afford me, companies that were reducing headcount, not hiring!
Suddenly I was competing with Ex-Meta, Ex-Microsoft guys for App Development jobs and while I have a great deal of experience I don't have a FAANG role on my CV and with the current market it doesn't look like I will any time soon. I'd put my consulting experience at Booking.com along side any of those 'prestige' FAANG companies as far as delivery, quality and process is concerned but I don't think hiring managers know that and it really wont help me get a job in these tough times.
Acceptance.
Well, looking on the bright side I suddenly had a lot more free time than I used to have and If KCD and Matt "Typescript Wizard" Pocock both say Agentic Coding and AI is fantastic, who am I to argue? I decided to go back to school and catch up on some of the things I'd been too busy to study up on - ATProto was one item on my learning to-do list, but the main one was investigating Agentic Coding to see what all the fuss was about. I dipped into my savings and bought a AI Hero licence and started working on a simple chatbot as a learning project (you can read about that here and see it in action here on my portfolio site - click the "Personality Construct" widget in the corner). In itself, writing an AI chatbot is very simple stuff, not much to it - wiring a chat interface to an API is nothing new - but the process used during the tutorials themselves, the methods and the way the instructor was using AI is what really opened my eyes to what was possible.
My experience so far had been telling an AI, via a prompt, to do something specific, a task (e.g.. "Write a test for component.tsx and aim for 100% test coverage") and then getting mixed results back (16/24 test passed) - then manually fixing the rest myself. But the way these guys were using AI was different, they were having a dialog with the agent. Instead of issuing demands to complete isolated atomic tasks, as developers they were telling the AI what they wanted to achieve! The AI was then describing how it would go about it and asking intelligent questions where it had knowledge gaps of what was being asked of it or how it should go about it. It was incredible to see a machine reason and deliberate on things, ask intelligent questions and put forward alternative solutions and raise concerns - this was genuine Sci-fi level stuff! This was lightyears ahead of the kinds of results we were getting with the third-rate AI tools we were provided in my last role. I got myself a Github Copilot licence straight away and got to work (I should say, I later migrated to Anthropic's Claude Code, generally considered the best in class AI Coding Agent at time of writing).
Not only was there suddenly a Deus ex machina talking to you about what you want to do, it could genuinely understand your plans and requirements as you have described them. It could assess your code, reason about it, spot bugs and gaps, identify issues, propose solutions and with some additional skills installed, create a fully documented development plan, write tickets for tasks and then spawn multiple agents to work in parallel on those tasks. If you want it to, it will happily work on those tasks using TDD; because the plan is fully formulated before a line of code is written it can write tests the completed code can be run against once its generated - try demanding a fully formulated solution design from a BA or PM before you begin implementation, in most organisations and you'd be laughed at or fired.
Moving Forward.
I have a habit that has developed over the course of my career. If there is a new skill I want to learn, or a new library I want to get some experience with I will often build on top of, or entirely re-write my custom CMS using that skill or technology. Waaay back, in the early 2000's, the first time I attempted this was when I wanted to learn PHP - at the time I was working as a web designer: Adobe photoshop, Macromedia dreamweaver and notepad were my tools of choice. Wordpress was king and making my own custom CMS felt wildly ambitious - only knowing a bit of jQuery, HTML and CSS, I literally did not know where to start - but I wanted to learn more about server-side languages and PHP seemed like a good bridge from where I was to where I wanted to go, so I set out to build a custom CMS project which I called Scribe CMS using HTML, JS and CSS and PHP which at this point was a mystery to me.
After months of googling, burrowing through stack overflow questions and lots of trial-and-error, Scribe CMS Version 1.0 shipped. It had an article editor (using Tiny MCE, remember that?) and a fully featured image library which you could upload images to and use them in your articles. I learned so much in this project because I was going outside my comfort zone, stretching myself and attempting something that I had no idea how to achieve. It was such a rewarding experience that I decided to do this more often and so that's how I developed the habit of rewriting my CMS whenever I want to learn something new or gain experience with a new technology. Since then I have rewrote it a number of times, once to learn React/Redux, I rewrote it again to learn relational DB and ORM with Apollo and Prisma, again for deeper practical experience of SSR with Remix (Now, React Router Framework) and now, once again I have started from scratch and built ScribeCMS v5 to learn about ATProto and how to leverage Agentic Code Assistants to streamline development process. It has drastically exceeded my expectation. I have to say, its been a revelation.
Content is King.
On average, whenever I start one of these ground-up rebuilds of Scribe, its usually about a months worth of work to get from npm init to MVP (basic auth, site config, articles, groups and an image library) but with Claude Code on my side I got this done - to a very high standard - in about 5 days and the scope of what I wanted was larger than usual too: For a while, since reading about the ethos that motivated the development of ATproto on Dan Abramov's Overreacted, I had been thinking of starting a new Scribe CMS implementation that utilises BlueSky/ATproto PDS for storage of content instead of the usual SQL/noSQL database I usually reach for. Without writing a line of code myself, just prompting carefully and managing what gets committed, I managed to ship BlueSky Authentication, Site Management, Article Group Creation and Article Management (via Drag-and-Drop), A fully featured Image Library all delivered and fully tested comfortably within a working week. Incredible results, far beyond what I would have thought possible.
You've probably read about, or at least seen the memes about, AI/Vibe coding producing slop and it can certainly do that if you don't stick to the principle of Garbage-In / Garbage-Out. If you've worked as a senior or team lead you will have learned the hard way the value of code reviews. Reviewing the code produced by AI agents can be as instructive as any tutorial: If, when writing your prompt, you have failed to mention something important or if you make assumptions about what the agents will extrapolate from the code you may find yourself catching issues that might seem obvious to you but the agent will be oblivious of. They can infer requirements but only from the information it is given or from extrapolating intent from the code.
Let me give you one example from my experience building Scribe CMS v5: As a user, there are 2 ways to publish an article in the UI:
- You can click the "Publish" button on the article you want to publish from the Articles list and select where you want to publish it via the modal provided.
- You can drag and drop a unpublished article into a group that belong to one of your sites.
This gap in the way I was using AI surfaced when I was implementing a 'createdAt' and 'publishedAt' timestamp feature. I explained in the prompt to Claude quite clearly what should happen when an article is created and when it is published: simply put, timestamps should be added to each entry in the PDS when they are created and published respectively.
It implemented this perfectly satisfactorily, I reviewed the code and it looked perfect so we shipped it. But the problem was, the implementation only applied when the user clicks the "Publish" button, not when using the drag and drop method of publishing. Or to rephrase it in a way that exposes the problem: Claude inferred that to publish an article a user would click the "Publish" button but it (quite reasonably in hindsight) did not infer that the drag and drop action (which seems quite implicit from a user-action/UI point of view, but is completely invisible to Claude from a code analysis point of view) - there is nothing that says "Publish" in this action so Claude simply did not implement the 'publishedAt' timestamp there. This is where context files are critical. A context file is where (among anything else important) you can define a number of ways in which a particular action might be accomplished beyond the obvious, if I had detailed this in my prompt or in the CONTEXT.md file, Claude would have known to make this change in both places in the code.
There are a number of ways context files in your repo can inform and streamline things, another worth mentioning is UBIQUITOUS_LANGUAGE.md - this is a Domain Driven Design concept which predates AI by about 20 or 30 years but is perfect for getting you and your AI Agents on the same page. In this file you would define what specific concepts are used throughout your app and the names that you give them, it acts as a shorthand for concepts so you don't have to spend 2 paragraphs explaining, yet again, the relationship between SITE -> GROUP -> ARTICLE. You can see an example of this here. Using context files like these are the difference between your code assistants context getting swamped with unimportant information, degrading the performance of your Agents while burning through excessive tokens and your Agent being fully on top of the progress of your app. You should manage and maintain your context files as carefully as what is getting committed.
Context Is Key.
There was one incident that really solidified the importance of managing your context files, it happened when we undertook a huge rewiring of the underlying schema (or 'lexicon' as they are called in ATproto) for the data we were storing. The original implementation we made when we first built the Scribe data storage solution involved the creation of 2 entries stored in the users PDS: One was "app.scribe.site" (which stored the sites and groups data) and the other was "app.scribe.article" (which stored all of the content and data relating to the articles themselves). The former contained references to the latter setting up the SITE -> GROUP -> ARTICLE relationship mentioned previously.
After a few weeks of working on this I happened to stumbled upon a community developed ATproto lexicon called "site.standard" (read more here) that had been produced for the exact same reason I had created the "app.scribe" Lexicon - specifically the storage and management of sites and documents on ATproto PDS's. It had been developed by and adopted on a number of other sites such as OffPrint, pckt and Leaflet to name a few. The similarity to the lexicons that I had developed was reassuring - I knew that as both myself and the site.standard developers had come to many of the same conclusions that what I was doing was right. Due to the large overlap of both app.scribe and site.standards Lexica, I made the decision to migrate over to this emerging site.standard now before I started adding more features to ScribeCMS.
The migration to site.standard was smooth and passed without incident, ("app.scribe.site" and "app.scribe.article" had been replaced by "site.standard.publication" and "site.standard.document" respectively) but a few days later, while implementing some tangentially related feature Claude started referring to the "app.scribe.article" lexicon that we had just finished migrating away from! This was a deeply frustrating problem, and the root cause was that there were still references to this old lexicon in the context files that claude had generated during development. The solution was straight forward, I had Claude do a thorough audit of all the context files to remove stale information. The lesson you should take from this mistake is: neglect your context files at your peril!
Skills that pay the (token) bills.
During the process of learning how to better leverage Agentic Code Assistants I started installing and experimenting with skills developed by trusted peers. Matt Pocock's Skills repo is remarkable and has enhanced the already impressive features of Claude Code massively. Matt does a better job of explaining what these skills do so you can view his videos on the topic here, but I wanted to highlight just how these act as a multiplier for developer productivity: At the time of writing, the repo for Scribe v5 is quite mature, its fully featured, with a dashboard, sites, group and articles sections, an image library and an analytics insights page. It is fully driving the content on 3 external sites and is a production-ready project, but there's always room for improvement right? So before I started writing this article I ran a Matt Pocock skill called /improve-codebase-architecture on the entire CMS repo. While I began writing this article, Claude went off an did a thorough analysis on the entire code (hundreds of typescript files at this point), assessed them all and produced a report on areas it identified for improvement, you can see the report here (TODO: Link).
It rates its recommendations from "Strong" to "Speculative". This report produced two Items that it highlighted as "Strong" candidates for implementation, giving its reasoning for (and against) what it proposes implementing and the benefits to be derived. In this case, Item 1 was a refactor to produce a Action Interface to replace the 6 specific actions on the site-item.tsx file, responsible for organising and editing articles. After giving it the greenlight to proceed with item #1 on the report it started an in-depth scan of what needs to be done, this produced a separate report (its a bit dry so I wont bother posting it here) with a detailed plan of what files will be touched and what changes will be made to them. It detailed a characterization test plan which, in summary was this:
- Write tests.
- Run existing code against the tests.
- If they pass, begin to refactor the code into separate testable action methods.
- Run refactored code against the same tests.
- If they pass - ship it.
It followed this pattern for the 5 places that this refactor was needed, first it produced a bunch of gitlab tickets (it integrates with glab seamlessly). Once it had these tickets it spawned multiple agents and started drawing up test suites in parallel, running the steps above and making a commit for each one of the five places this work was needed. While this was going on I literally had nothing to contribute to the process so I wrote this article.
After the work was completed, the test suite run, the tickets closed and the work pushed to a local branch, My contribution was to simply review the code it produced, push it to the remote and merge it. It took about 30 mins from initiating this code review to implementing the two strong candidates it recommended freeing me up to do user testing and actually use this app for what it was built for, producing content for my sites!
Its worth highlighting that AI code agents do make mistakes so DO restrict its Git permissions and DO review the code - these mistakes are usually due to a missing piece of vital knowledge rather than an actual error - a valuable lesson worth taking to heart: Its better to do a 4 paragraph prompt that provides the full context in excruciating detail to completely inform it of all the information that it needs to provide a solution. Almost all the mistakes it has made in my experience has been due to the prompt not being detailed enough: a short "I want you to do X, don't mess it up" will almost certainly fail. Being verbose, detailed and precise in your prompts and descriptions is a skill you'll learn fast using coding agents and its certainly less time consuming than the implementation being wrong on the first attempt or worse still, having to retroactively roll back what you shipped because the prompt you made wasn't detailed or accurate enough and your code review wasn't thorough enough.