Goodbye Software Engineering…Hello Product Engineering
(Hint: It was ALWAYS Product Engineering.)
History
“You are responsible for the assembly your code creates.” - Mike Acton
So about 20 years ago, I was at Insomniac Games and I was attending a software lecture by Mike Acton, who said the above quote during the meeting. I was very early in my career and had just gotten done with my second (and final) game with the company.
And, to be honest, the above quote intimidated me. I was just getting good enough to make sure that what I did at the C++ level worked (and trying to get it done on time). The thought have having to then go and check everything that my code produced at the assembly level felt like another world.
I don’t think I was alone in this sentiment, as the general discussion continued onto how we were doing work. Someone made a crack about Fortran in the meeting, to which everyone laughed, with the exception of Mike. Completely deadpan, Mike responded with “What’s funny about Fortran…it creates incredible assembly.”
Needless to say, this left a mark.
Practice with Assembly (or More to the Point…Not)
Admittedly, I really did admire all the folks that were focused at the lowest level (my business partner Rob Wyatt included). And a big part of me really wanted to be that focused there, figuring out how to make it run faster, always.
I would love to tell you that from that moment on, I was diligent about checking my assembly after I wrote my C++ code.
I didn’t.
Mostly I still had my head up my own ass about the style of the C++ code, did it look good, did it seem maintainable (for whatever the whims of a given place dictated that to mean). And to be fair, it was the main critiques of code reviews at the time (and largely ever since).
To my credit, I will say that my code did get the job done, whatever it needed to be.
And this was despite the fact that I never looked at the assembly!!!*
(*Except that one time where I needed to understand why the strict aliasing optimization was failing on some older code and had to explain it. Thanks to Mike Acton for that tip as well).
But, how?!? How is such a thing possible?
Well, because the code produced the result that I wanted it to. The behavior matched my needs, I could write tests against it (when I wrote tests), and it worked (mostly) without bugs.
So I just moved onto the next problem, confident that I have solved the problem.
AI in Development…Is This a Joke?
With that out of the way, let’s fast forward to today’s world. There seems to be a big debate around whether or not AI models are going to change everything for coding, mainly centering around two camps of “these are all just toy models and the code it produces isn’t worth a damn” to “oh shit, this is changing everything.”
To be honest, this time last year, I was definitely in the first camp…albeit without having really dived in to understand what was happening. I saw the videos online and largely brushed them off with the same sort of arrogance that I used to judge other people’s code.
“Well, stylistically it shouldn’t be like that…”
“Wait, it isn’t even separating them into different files? Hah, I could do a much better job of making this more modular.”
“Sure, it is fast to develop…but will it be maintainable by humans?”
Over the decades, my instincts have been trained to look at code, having the equivalent of a compiler and runtime running in my head to look at the code and see where it could go wrong. To be sure, this is what every good software has done (…or just smashes the compile command enough times and fixes stuff until the job is done).
As code reviews came in, I was given instruction on how to restructure it, make it better, make it this or that…and I did it…believing that this was the end-all-be-all. Hell, it was what I got rewarded for as so many places.
But I missed a big point about all software development.
It’s the Customer, Stupid
As much as I can claim to have “mastered” the art of code development (and there many that would claim that I had not achieved that by any stretch), there was something fundamental missing.
I would love to believe that the code that I’ve written is the ultimate combination of language mastery, insightful use of algorithmic tricks, and, frankly, art. To be honest, most programmers want to believe this. We are a master class of doomsaying in our analysis of code (especially other people’s) and supreme arrogance in the righteousness of our own code and products. We love to show off for the other programmers to show how badass we are.
But there is one fact that we typically miss: people don’t pay you for your code.
Ever.
Yep, I can hear you all screaming now, “but we develop this way so we can easily maintain it” and “there is no way a computer can be as insightful as I am” and “this guy just sucks.” And while I’ll cop to the last point readily, none of these (and more) actually matter.
What matters is whether or not the functionality that you produce actually matters to other people.
Does it solve a problem for them?
Does it create a great experience for them?
Does it save them time?
Does it create a great product?
Does it make them happy?
Will they pay for it?
It is the complement of Mike’s quote above:
“You are responsible for the functionality that your code produces.”
It’s Always Been Product Engineering
So earlier I admitted that a year ago I had been firmly in the camp of “AI…what a funny little toy.” Today I’ve drunk the Kool Aid and am now in the second camp (the one that says: “oh shit, this is changing everything”).
And the reason for this has a lot to do with why I don’t check my assembly.
I started dabbling with AI development, helped by a useful video that expanded my understanding for how I could actually use the tools. It was like having to unlearn everything I had learnt…I just could not get my head around everything what the code was doing…and I felt like I had to…because that was what good engineers do, right?
Then it clicked…maybe it didn’t matter what the code looked like. Maybe I could focus more on whether I was getting the functionality I wanted and less about the code.
(As Rob points out: “After working as a contractor on tons of code bases I don’t control, I always say that code is just text in a file, compiler doesn’t care about your fancy formatting and spell checked comments.”
In other words…it does not affect the generated assembly or the functionality one bit.)
With that in mind, I started tearing into the development system, attempting thing that I would not have previously…some thing that were not possible previously:
Developing a UI from an image
I’m no expert in CSS and HTML (nor will I be)…but I could just drop an image and let the robot create it for me
To be sure…it did not get it right the first time…but I could art direct the CSS and HTML (and later PHP) by just telling it to the robot
Commanding the creation of a Backend Service
I’m not an expert in writing Python…but by giving just enough info about the behaviors I wanted, the robot could create a very useful service for me.
Again, where it needed augmentations, improvements, or fixes…I just told it what to do
Creating a silly game from clip art and sounds
I asked the robot to create a simple side scroller…adding bits and pieces here and there
Generating music for the game using Udio
And dammit…it worked…
Fundamentally, I started caring more about what I was to achieve, rather than how it was being done.
And honestly, that is what our customers care about as well. Not about how it looks in code…but what it does…and how fast I can tweak it, make it better.
To be sure…it is fast. And now I don’t have to worry about whether I have learnt all the languages I need to make something work.
Hard Stuff is Still Hard…but Maybe a Little Easier
Now, before you believe that it is all fun and games…I’ll be honest and agree that “yeah, it is not all there yet.” There are times when the robot gets messed up, hooked into loops, can’t figure its own code out.
So sometimes it is worth sweating it out a bit, falling back on those decades of engineering experience…and sometimes I just say “fuck it” and start all over again…after all, it might have only taken 20 minutes…and I know what I want now…starting from scratch isn’t so bad.
(And let’s face, it programmers…how many times have you walked into a code base and said “I don’t understand what the hell these folks were saying…I have to rewrite all of this to make sense.” Is this really any different?)
To be sure, product-market fit has always been hard…it has been hard to bring something up to speed, get to a set of customers, get them to try it…see if it actually would do anything for anyone. And the amount of effort to get something up and running has been herculean.
Figuring out what customers want is a challenge…because a lot of times they don’t even know.
But imagine a world where you are focusing on the outcomes vs. the construction. Imagine that world is far easier and far faster to get something up and running that let’s you test your hypotheses for the “next great product.”
Imagine a world where we’ve shortened the distance between the product person and designer and the actual product. Because that is the world we are heading to increasingly fast. Where your idea can come to life faster than ever before.
And that’s a great thing. Because that means that someone doesn’t need to know all of software engineering (frontend, backend, databases, etc.) to do something amazing.
It is not perfect today and I would be remiss to make you believe that it all going to be easy. It isn’t. But much in the same way that compilers and software changed the needs of the underlying assembly and architecture, you can bet your ass that everything will change with this new type of development. We’ve already architected new types of processors…how much longer do you really think it will be before we’ve rearchitected new languages and frameworks to better meet the needs of developing through AI.
And that just keeps making everyone into a product engineer.
To The Diehard Coders
To be sure, I don’t want to go back to the old way of working…I admittedly want to be focused on the products I create and not how I have to slog to get there.
Now, for those of you that still want to be coding in the same way that you have, I respect you. Honestly and truly I respect you because I understand where you are coming from. Because I’ve been there, I grew up there.
I get the pride. I get the amazing work that you’ve done. And I get the desire to keep being amazing in that way.
But I have a bit of advice for you…which takes us back to the very beginning: I hope you are checking the assembly that your code produces.
Because if you aren’t…then you are accepting that “it just works” below whatever level you feel most comfortable working.
And doesn’t that describe everyone working with AI to create products today?
(Ironically…this was written without the help of AI…unless you count autocorrect….)
Addendum - Talking About Optimizations
Rob rightly pointed out the following: “does this mean we no longer care about traditional performance metrics as long as it's fast enough? small enough? What about optimizing for power? Are we going to start getting generally bad code because the robot made something that technically works?”
So, this is a great point about whether we are going to push important performance metrics by the wayside…and actually, I’ll argue that the exact opposite will occur.
Questions around any performance metrics are fundamentally about behavioral constraints. That is, when I am saying “I want this faster, I want it smaller, I want it to use less power”, I am not making any statements about how I do this…I am back to asking what I want to happen.
I will fully cop to my early assertion about holding onto code as a form of art…preciously crafting my class structure in some way like I was Leonardo da Vinci…but really not giving a damn about performance as long as it “felt” good (and admittedly was pretty extensible for what I was using it for).
Because I held onto the code as a form of art, I wasn’t doing any kind of measurement around how well it would perform, let alone whether it was small or how much power it used.
But what if you didn’t care about the “look” of the code, but instead what it did, like I’ve been saying all along. Then you just add in your constraints for speed, size, power…in each of these cases, they are all *measurable constraints…*the kind of things that an optimizers can go nuts with.
In a world where AlphaGo invented new moves for Go and JIT HotSpot compilation exists, it is not a far fetched world to imagine a combination of an LLM and a compiler to loop through and optimize the hell out of whatever functionality with we give it on a particular architecture.
To ground this in a real world example, let’s use my earlier example of the Rust ray tracer. Now, developing a ray tracer today in a world of RTX and MoonRay is a bit silly but, it does have some nice characteristics:
It is simple
It lends itself to testing out lots of different optimizations
It generates a fun result (i.e. an image)
It makes me feel like Kevin Flynn (if you don’t get the reference, I pity you)
After I got it working, I made it a bit interactive, but it was slow as molasses (I did exactly what I ask, which was to create your run of the mill ray tracer, as simple as can be). Curious if I could make it better, I asked it to make it faster.
The robot jumped at taking advantage of multi-threading to parallelize the ray casting, which was a big bump in performance (~2 fps to ~20 fps). Then I asked it if it could make any further improvements, where it added in acceleration structures (don’t quite remember the bump then).
(To be fair, when I tried to get it to optimize for ARM using NEON instructions, the output image started to fall apart…so it isn’t quite all there yet.)
Nevertheless, despite being an admittedly simple example, it does go back to the original point…because I cared more about the behavior than the mechanics, I was able to get the robot to keep pushing the performance.
Who knows, if my belief in LLM + compiler is right, then maybe the language of the future is actually…assembly.
Next Up - PJ’s Money Where His Mouth Is: Shipping an Application That People Pay For (Can he do it?!?)
Big shout out to Hans Ku and Maggie Wiedner for handing me some Kool Aid on AI development.