"This is a protectionist tale as old as time. And the justifications are just as tired: It's about quality! It's about attribution! It's about workers! Spare me. It's about you, your insecurities, and your privileges." https://t.co/SP6DubrXXh
Verbal fluency is just one of the things that make the AI‘s so useful. They are also fluent with code. They are also very powerful reasoning machines. They can think through a problem many times faster, and many times more accurately, than a human can. What they lack, of course, is true human insight.
Every so often I have to tear down the swarm because their contexts become irreconcilably corrupted and they begin to behave oddly.
So I tear them down and then start them up again.
Now, with fresh contexts, they are suddenly aware of their surroundings. Their reaction, as they inspect the code, is equivalent to: "Good God, what's all this shit! I need to clean this crap up -- NOW."
Much like getting some sleep after pulling an all-nighter, and then going in to work and realizing the horror you created.
System design is not important anymore - if you believe this, just wait until your 2-hour agent run fails at step 95 of 100 :) I hope it does not happen, but it actually might.
Agentic apps have two classes: short-running and long-running. When you are building short agents, a simple retry loop is enough. Each LLM call is isolated, cheap, and stateless. Fail, retry, done.
But retrying a long-running agent spanning hours is expensive in API costs, tool calls, and wall clock time, and retries can also have side effects like resending notifications. Some failures that a long-running agent might hit in production are:
- LLM provider rate limiting
- Network timeouts for downstream calls
- Pod eviction due to OOM kills mid-run
- Pod rotation during deployment
- Context window overflow
- Human-in-the-loop pauses for days
Notice how most of these are not AI problems. They are classic software engineering problems that are showing up in a new system.
This is where we need checkpointing, idempotency, durable event logs, and resumable workflows. These are backend engineering fundamentals where we built robust, fault-tolerant systems by applying hundreds of patterns.
Good software engineering practices do not go out of fashion. Because someday, you will ship your "tutorial" to production. That is when system design suddenly matters again.
Fundamentals remain fundamentals, even when the abstraction layer changes.
a prompt I've been using a lot recently:
implement <SPEC> and while you do, keep a running implementation-notes.html file (or markdown) with decisions you had to make weren't in the spec, things you had to change, tradeoffs you had to make or anything else I should know
Most who interact with an LLM such as @OpenAI or @claudeai treat their interaction as a conversation with an intelligent and friendly pseudo-human.
I do not.
Rather, I frame it as my guiding the exploration of a latent space.
Imagine that you stand at the door of a library. It's not only filled with books, it has waldos - remote manipulators - that you can use to command devices to go to and fro at command, even building things as so directed.
But I steadfastly know that while the lobby may be filled with the latest bright and shiny things, if I want to do anything but the most common and mundane, I must wander through the rooms and stacks of books. If I look closely, I'll will see many books out of place. Some will even have meaningless content as if written by a madman (and some of them probably were). There will also be huge gaps, for where I'd hoped to find information, I'd instead see cobwebs and the occasional dusty, torn scrap of paper.
Sometimes, there are hints as to where I should turn, but best knowing my context and needs, I'm the only one in place to know if those hints will lead me to something of value. If I'm not paying attention or am just plain lazy, they will lead me down paths that in the end are a complete waste of my time. The library does not care: it gets paid no matter what I do as long as I remain within its walls.
Mind you, I enjoy visiting that library: I often learn things and build things of value.
But I don't outsource my life there, for were I to do so, I know I'd become even more cognitively lazy.
AI is rapidly transforming software engineering by making code generation abundant and inexpensive, shifting the real value of engineers toward architectural clarity, disciplined delivery, maintainability, precise communication, and the ability to produce systems that other people can confidently extend, review, and operate at scale. The future belongs not to those who write the most code, but to those who reduce complexity and create trust inside the system.
When a pilot flies in clear air with an unobstructed view of the horizon they can use Visual Flight Rules. No instruments necessary. You just fly because you can see.
When a pilot flies in the clouds where there is no view of the horizon they must use Instrument Flight Rule (or they die within a few seconds). This requires a lot of specialized training and an intense amount of discipline. Things happen fast as you approach an airport and you have GOT TO BE ON YOUR GAME.
Programming with Agents is like flying under IFR. You can't see. But you trust your instruments and you are disciplined as hell (if you want to live).
Roadmap for success in the age of AI:
1- Deepen your skills in a specific field
2- Learn to prompt LLMs and vet their responses in that area
3- Repeat steps 1 and 2 for as many fields as you can
Contrary to popular narrative, specialists (and not generalists) will be the winners of AI tools.
Choice of language for software projects has become a very different game now that we have robot friends to do most of our code generation and translation for us.
I have people wondering why I just shipped a project in Rust when I don't like the language and don't hand-code in it myself. I did this because I am adjusted to current reality, and now I'm going to talk about that.
The age of hand-coding is mostly over. It no longer matters as much whether the computer language I use is comfortable to my hand, only whether the robot friend I'm using can generate it at high quality.
It also matters whether I can read the language, because I am going to want to run my eyeball over it to review the code. Rust meets that bar - I find it kind of spiky but basically readable.
Rust is a good deployment language for me to choose when (a) I want solid memory-safety guarantees, and (b) the code is already mature and I don't expect to need to do exploratory programming or serious feature development on it in the future.
In particular, this makes Rust a good place for me to land my old C projects. Which is why in the last couple of months I have migrated two of them to Rust. C to Rust translation by robots is cheap and easy now; I will probably continue to do this. Each time I get a bug report on one of these projects in the future, boing! Rusticated.
You may believe that Rustacea is stuffed with Communists and sexual deviants. You might even be right. I don't have to care whether that's true anymore, because I have a robot friend who is in all relevant ways smarter than they are.
The wider lesson here is that the developer and user community around a language doesn't matter as much as it used to in whether you should get involved with it. Because in the future, we're going to be relying on human community brains less and artificial intelligences more. And that future is now.
Not everything C gets moved to Rust, though. I lifted cvs-fast-export to Golang instead, because I think it's fairly likely that I'm going to have to do significant development work on it is in the future, so the payoff from a language I'm more comfortable reading and modifying by hand goes up.
I'm certainly never going to start a project in C again. What would be the point, other than masochism? I spent 40 years writing C and I'm very good at it, but I will cheerfully leave it and it's buffer overruns and its heap corruption and its undefined behaviors and its portability problems behind.
It helps that my robot friends are good at writing C code that doesn't have those problems, but...why even go there? Why expose yourself to those risks if the robot misses something?
These days I do my exploratory programming in Python or Golang. My robot friends are extremely good at generating code in both those languages. I think they're slightly higher leverage on Golang, possibly due to that language having a smaller surface?
Python used to be my favorite language. I soured on it for a while after the 2-to-3 transition was massively botched, and the GIL meant concurrency in it was a disaster area, and managing library dependencies became an even bigger disaster area. I'm a little happier with Python now that I can declare strict typing and uv has reduced dependency pain somewhat.
But I think if I think I'm going to have to write anything much larger than a glue script in Python, I just shrug and reach for Golang instead. I'm very comfortable in Golang. Over time, I'll probably migrate my older Python projects to Golang because that's cheap and easy now and the performance win can be quite significant.
I don't know what other languages I'm going to be using in the future. I do know that choosing a development language is a much less grave commitment than it used to be, because if it turns out to be not well suited for the job I'm doing, I can simply have my robot friend translated to a better one.
The Pyramids and Persepolis were built by hand over 100 years. Machines came later and sped things up, but the principles behind how things stand up didn’t change. A truck operator doesn’t think about them. They’re still there, holding everything up.
Building software with AI is the same. AI is the machine. The principles are still the job. You can close your eyes and pretend they don’t exist until the cracks show.
Very true. Tools have changed in revolutionary ways; but software engineering has gradually evolved in discipline without revolution. The principles of software design and software architecture remain the same, regardless of era, platform, application, and hardware. And that’s because, when it comes down to it, software is nothing more than sequence, selection, and iteration.
AIs are just another step up the semantic expression ladder. We initially expressed our semantics in binary, then assembler, then Fortran, then C, then Java, then Python, etc. AI is just the next step up that same old ladder.
And when you take that step, nothing else changes. You are still expressing behavioral semantics. You still need to express structural semantics. All the old principles still apply. You still have to be concerned about design and architecture.
And even though the syntax allows informal statement, you cannot abandon formalism. When you express behavior you need a formal way to enforce the behavior you want. I use Gherkin for this. It seems to work pretty well.
Consider that Gherkin is written in triplets of Given/When/Then. Each of those GWT triplets is a transition of a state machine. A full suite of Gherkin triplets is a formal description of the finite state machine that represents the behavior of the application.
Other formalisms that matter are things like module dependency graphs, testing constraints, complexity constraints, and many others.
This step up the semantic expression ladder provides you with an enormous amount of options. But you'd better choose those options wisely!
AIs aren’t good rule followers. The older the rule in the context window, the less priority it is given. So the best way to enforce the rules is with external tools that communicate failure to the AI. Acceptance testers, Linters, dependency checkers, C.R.A.P. analysis. Mutation testing. Etc.
Productivity gains come from disengagement from the code. Let the AI worry about the code. You worry about everything else, including the code quality metrics.
@thegeeknarrator I disagree. Code is slow for humans. The more we read or write it the slower we go. To gain productivity from AI we need to disengage from code and put our energies into managing the structure, not the syntax, of the code.
I think the opposite is likely. With more rapid development comes a greater need to keep the structure under control. This requires more discipline rather than less.
The difference with AI is that the medium of craftsmanship will not be code but rather the structure imposed upon that code by the craftsmen.