Plastic Forks and Paper Plates: When AI Writes Code
22 Jul 2023
22 Jul 2023 by Luke Puplett - Founder
After 38 years writing code, I've come to conclude that there are only two things that define good code, in order of importance:
-
Is it easy to change?
-
Does it work?
-
Bonus: does it work, well??
To help convince you on this point, indulge my little digression.
Once upon a time at a financial gig I was working at (they're all finance gigs because in Britain we literally have no other industries that write their own software, something that could change if AI makes it cheap enough) we hired a new guy. As it turned out over months, he was an excellent programmer. In his first week he got stuck in and delivered some code. It was a web MVC style API and he’d put all the code in the controller action, with no abstractions, just sitting there, line after line, in the controller action.
It was all tested and it worked perfectly and was to spec. Me and the guy running the team, (who was an excellent manager), scratched our heads and didn’t know what to make of it. We just wouldn’t have turned in work like this. It was, to us, like how someone with no experience would write something. But it worked and it was easy to understand. Very easy. Too easy?
We went round and round talking philosophically about whether this was our problem, all in our heads, and that it was actually what good agile code looked like. Or it was awful. We talked in an empty office until I, with a long drive ahead, left him to make a decision. I wasn’t the boss, you see.
He made the call and sent the work back and asked him to revise it. He made some abstractions and sent it back and it was accepted. Over the course of months, the wider application became monstrously complicated for what it was. I think about this all the time. That code was perfect and that programmer was excellent and it messed with and challenged our preconceived notions of good code, which were cargo-culting convoluted enterprise garbage.
As I build Zipwire I’ve gotten in to a different crowd. These are entrepreneurs and often bootstrapped solopreneurs. These people almost never discuss code or architecture. They hack things together and make money very quickly, or kill their app at the first sniff that it’s a dud. They don’t write tests, or few, many have only just learned to code and they most certainly would chuck code that works straight into a controller without any further classes, abstractions or interfaces.
What’s important is whether the code can be easily changed. It’s obviously important that it works, but if it doesn’t work and it’s hard to change, then you realise it being easy to change is the primary goal of code. Hard to change could be its convolutedness (convolution?) or dependencies on something else that’s hard to change, or massive friction in releasing the code (such as bureaucracy), or a lack of a way to know whether your changes haven’t made anything worse.
It can also be the presence of tests. This is where automated testing has led everyone astray for 20 years. Everyone tests the private implementation of a system, and not the public interface and its functionality as available to outsiders.
With AI-generated code, the ease with with you can change code has some interesting consequences.
Loss Aversion
One of the biggest impediments to improving code is sunk cost - the reluctance to throw away things we've spent a lot of time on, even when they're doing us harm. This often applies to tests and abstractions.
With traditional hand-written code, we invest substantial time writing tests to cover our complex implementations. These tests couple us to those implementations, making refactors difficult. The tests become their own legacy codebase accumulating cruft over time. With instantly generated code, tests can focus on validating the external behavior, not the internals. We can rewrite the internals freely without breaking tests.
Likewise, we often create unnecessary abstractions because we feel obligated to continue using things we previously built. The abstractions accumulate until the codebase is a convoluted mess. With instantly generated code, there is no legacy abstraction burden. We can generate simple, clean code for each new requirement instead of perpetuating a tangled architecture.
Newcomers to a legacy codebase have an advantage here. Unencumbered by the team's sunk costs, they can make dramatic improvements by throwing out junk and generating new clean code. The ease of doing this with instant code generation essentially lets every developer have that "fresh eyes" and "wasn't my sweat" perspective.
No Speculation, No Cry
A lot of code we write is speculative - interfaces, frameworks, and prototypes that attempt to predict future needs instead of solving current problems. With instant code generation, we don't need to speculate about hypothetical future requirements. We can generate exactly the code we need, when we need it, for the requirements we actually have. This greatly reduces over-engineering and unused code.
In summary, by removing sunk costs and reducing speculative code, instantly generated code helps ensure we have only the code we need to solve the problems we actually face! This keeps the codebase lean and malleable. The ease of generating new code reduces inertia, enabling regular rejuvenation of the architecture.
Plastic Cutlery
With traditional coding, we sweat for ages over code that then becomes immortal junk we're stuck maintaining forever. It turns into inertial concrete making it hard to change things later.
With instant code you can throw just enough together to try something out, with minimal effort. If it seems decent, great, expand on it. If not, select: delete.
This fail-fast approach lets you rapidly iterate without feeling the need to pull out a subclass and five interfaces and form an unhealthy attachment to it all. To be honest, you’ll still feel this need, because we humans don’t actually tend to change our minds when confronted with invalidating new facts and circumstances, but give it time.
You can also bin chunks of production code instead of gradually morphing existing classes to fit new needs. It can be simpler to just whip up a new component or service from scratch. Replace old code instantly with new code that’s has little value bestowed upon it via The Ikea Effect.
"Systems can evolve via replacement not accretion."
You still need some design chops and foresight to avoid a tangled mess. But easily tossing code and whipping up new stuff makes software soft again like clay. Systems can evolve via replacement not accretion. With instant coding you can refresh chunks of code whenever, ditching unnecessary complexity that's built up.
The motivation behind the frameworkification of code and creation of libraries is DRY (Don't Repeat Yourself) coding. But DRY can hinder agility when abstractions must be learned and maintained, and all that other code gets bound to it. With instant code generation, we may see a reversal towards more WET (Write Everything Twice) code. The don't-repeat-yourself mindset came, partially, from hand-coding's costs. But with code generation, duplication isn't a burden. Describing requirements multiple times in different contexts may be easier than creating and learning shared abstractions. So instead of DRY libraries to avoid repetition, we lean towards regenerating code for each use case. The ability to instantly discard and regenerate code reduces the value of pre-built abstractions.
In summary, fast and cheap code creation and replacement means code stays perpetually pliable. The inertia leading to bloated, flimsy systems is reduced. You can build flexible, long-lived software by treating all code as disposable cutlery.
That's lovely and everything but what is Zipwire?
Zipwire Collect simplifies document collection for a variety of needs, including KYC, KYB, and AML compliance, plus RTW and RTR. It's versatile, serving recruiters, agencies, people ops, landlords, letting agencies, accountants, solicitors, and anyone needing to efficiently gather, verify, and retain documented evidence and ID.
Zipwire Approve is tailored for recruiters, agencies, and people ops. It manages contractors' timesheets and ensures everyone gets paid. With features like WhatsApp time tracking, approval workflows, data warehousing and reporting, it cuts paperwork, not corners.
For contractors & temps, Zipwire Approve handles time journalling via WhatsApp, and techies can even use the command line. It pings your boss for approval, reducing friction and speeding up payday. Imagine just speaking what you worked on into your phone or car, and a few days later, money arrives. We've done the first part and now we're working on instant pay.
Both solutions aim to streamline workflows and ensure compliance, making work life easier for all parties involved. It's free for small teams, and you pay only for what you use.