As a (primarily) web developer, I don't have to think about mutexes, sockets, file descriptors, or other low-level details. Obviously those haven't changed, but the stack has moved up, and we've all moved up with it.
A similar shift needs to happen with AI. We're still technically shipping the same high-level code with LLMs, but it feels like we've taken another step (or three) up the stack. So what are the next set of primitives in the age of AI programming? Do we think in terms of components? Or maybe in terms of functionality/features?
Software development has been doing this for a long time. Each move up the stack gave us a new set of things to think in:
MachinecodeAssemblyLanguagesLibraries/ APIsPlatforms? now
This kind of higher-order thinking had already started before LLMs with things like libraries and APIs, but there was still significant work to be done to integrate them into your application. Is the difference now that we hardly use anything third party and default to building customized versions of these libraries in-house?
So far I've asked a lot more questions than I have answers, but I do have some proposals.
Thinking in... #
Components
When I say components, I'm not necessarily talking about UI components (we've already had a decent abstraction for that thanks to frameworks like React). I'm talking about the building blocks of your application. The things that are reusable and can be composed together to create a larger whole.
For example, if you're building a CMS, your level of thinking can start at content, files, users, roles, etc. Whereas before you had to think through each of these things in detail and painstakingly build them out yourself. Now with AI you can simply prompt it to generate content components, file components, etc.
PublishEditorial calendarContent, FilesUsers, RolesPostgres, S3
So instead of designing the whole CMS at once, you start with those four pieces. They have to live somewhere (Postgres, S3), and then you build features like publishing or an editorial calendar out of them.
This is a useful way of thinking, but mostly if you're building something that has been made many times before and already has well-established patterns.
This becomes much more difficult as you start to build something that is unique and novel. In that case, you'll end up fighting with the model more and it might be easier to just write the code yourself.
Features
I'll start this out by saying that I haven't seen this work yet, but it's worth mentioning.
You can technically prompt AI by referring to features instead of components, but unless you already have a working application to build on, the project will quickly turn into a huge mess. Without the context of an existing structure and patterns, the model will have a difficult time keeping things organized.
It's like telling someone to climb a mountain when they can only see 3 feet in front of them. They need the whole picture to plan their route.
This is exactly the problem with feature-first development. You end up with a codebase where everything is duct taped together. So if you start by asking for an editorial calendar in your app first, with none of those parts underneath, you're already on the wrong path.
Prompts
This is an interesting one. Do you need to stop thinking of software in the traditional sense and start thinking about "What context and descriptions do I need to give the model to get it to do what I want?"
That's not quite the right way to think about it. If you were communicating a solution to a human, what would you tell them? That should be the prompt (with proper context, of course). You're framing the problem and (ideally) also providing your proposed solution.
The "level" in which you're speaking still matters, though. At this point in the AI cycle, if you're speaking too high level, you'll get a generic solution that doesn't fit. "Build me a CMS" or "add an editorial calendar" is the same issue as thinking in features: without more specifics, the model only has its training to guide it, which is the average of all the code/data it's seen.
If you're specific enough (i.e. the parts, where they live, the constraints) this can be a great way to get the work done. And when the problem requires a very technical solution, you'll still need to dive into the weeds to better guide the model. Same as you would with a person.
Solutions Architect
As cheesy as I've always thought this title was, it's actually a pretty good way to think about solving problems with AI. It's both the right abstraction and right mindset to have when building. The part that matters here is the set of things those people already think in: components, platforms, technical constraints, and how the pieces fit together.
That's all of the previous sections I've talked about, sitting at one level:
- Create the application components that work together to solve the problem
- Compose those components into the various features and workflows that the application needs to support
- Figure out how to best communicate both the problem and the solution (i.e. the prompt)
- Consider the technical constraints, performance requirements, and the tools/platforms available to them
A solutions architect sits somewhere between software engineering, systems architecture, product/business requirements, and customer/stakeholder communication. They often aren't writing most of the production code, but they need enough technical depth to design something that can actually be built. That sounds a lot like the job now. The model will write a lot of the code, but you still have to decide what the pieces are, where they live, and how they compose.
I don't have to think about mutexes and sockets unless something is actually wrong down there. Same move here: I shouldn't be in the weeds on every data mapper and controller either. I should start at the concepts of content, files, users, roles, and the platforms they sit on. Then when I ask for a feature, it has somewhere to land.
This also gets at that question from earlier, about whether we just build in-house versions of libraries now. Kind of, for the middle of the stack. Integrating a package used to be work, and writing your own was a worse option, so you imported one. Generating a File module that works with this app is often faster than trying to mold a generic library into place. Obviously I still wouldn't rebuild Stripe or Postgres, but the helpers and glue code are now cheap to generate.
I still write JavaScript. The model writes a lot of the code in between. The level I need to get right is where I start: named parts, the platforms they sit on, and then a feature. If I get back a generic solution that's not a good fit, then I go into the weeds.