Slowing down to go faster
Using Claude, Copilot and other tools lets us build faster, but faster is not always more sustainable. In recent weeks, I've found myself slowing down to go faster.
Last week, I wrote about Antipatterns of Software Engineering with AI. In a sense, this is a continuation or different angle on the same problem, more specifically the AI propensity to over-engineer mediocre architectures with leaky abstractions, that end up being messy to maintain and extend. The initial speed sometimes (not always) comes at a cost.
Recently, I found myself paying this cost, despite using Fable5 as the driver of a large extension to an existing system.
In the abstract, I had a system that did X for type A. I wanted to extend it to do X with types B and C. There is a lot of complexity under the covers, and specific cases for A, B and C. I asked Fable to try to generalize over the three, and it did a passable, working job of it. But the implementation still had some smells: minor duplication, leaky abstractions simultaneously breaking SOLID, while having too many moving parts and unnecessary abstractions. It would certainly work for A, B and C, but we would be right back to even more duplication, abstraction and moving parts the second we want to add D and E (and in this case, I know they are coming, it’s not just guesswork breaching YAGNI).
Coding again, like a caveman
In the face of this, I decided to take a step back, keep some of what had been generated, but also aggressively delete a lot of it, and inspect the API machinery myself, to get a feel for how to make it more ergonomic to use for a caller. What Fable did worked, but was not optimally ergonomic, just as you would expect if trained on the average of code found on the internet: passable, just about fit-for-purpose, but mediocre.
The overcomplicated solution with quite a bit more code than strictly necessary was the trigger point to slow down, think through the problem thoroughly, and start sketching it out in code by hand to come up with a clean solution.
How much time did I lose by doing this? Conservative estimate, probably a days work of human time, in addition to the first 1-2 hours spent on the AI generated solution. I can’t yet quantify the downstream gains from improving the solution by hand, but what I have arrived at is a simple DSL-like abstraction for each additional use-case needed of maybe 100 lines of code, rather than the Cambrian explosion of complexity of multiple thousands of lines that the AI came up with.
It might still be that in the age of AI, the problem of the unclean solution is not really a big problem, less cognitive load on a human etc. But, with unclear intent, leaky abstractions, code duplication and larger amount of coding needed, we still introduce room for bad implicit assumptions and new defects.
The moral of the story
I don’t have the data to prove it yet, but I suspect my manual caveman intervention has reduced future work by a factor of 10-20x, whether done by human or AI, and likely the amount of lines required by a similar amount. Now multiply this by the number of occurrences this is done in, and you might save anywhere between 10’s to 100’s of thousands of lines of code, all which introduce cognitive load for human and AI alike, and the opportunity for subtle defects.
It’s worth remembering: all code is tech debt and a liability. It’s just that with AI, the debt and liability got cheaper. But if we pile it on at a pace that exceeds the factor of cost reduction, we are no better off, just digging a deeper ditch to stand in.
Does this mean I will go back to handcrafting all my code again? Absolutely not, that would be foolish. But perhaps it does mean that we should be observant of the triggers that should prompt us (pun intended) to slow down, and rely on our “old” skillset, it’s not quite antiquated yet.
Just as the Hare and the Tortoise found out in the classic tale: sometimes slow and steady (and sustainable) wins the race.


