Sorcery in the open: is generated code still source code?
The article traces the history of source code from assembly to compiled to interpreted languages, and examines how AI-generated code challenges traditional notions of source code, suggesting that prompts may become the new source, and referencing Knuth's literate programming as a possible direction.
7 min read
July 22, 2026
Categories
ai
coding
Tags
ai
llm
opensource
reviews
In the 1970s a bunch of academics started complaining when software was only delivered as a compiled artifact. It was kind of ignored by the companies trying to monetize their software, but it was taken up by a small group of people championing free software. By the 1980s, they codified their demands.
Then in the 1990s, the ubiquity of the home computer, mixed with the distribution channel of inexpensive consumer internet brought “free software” to the more common parlance, but “free” is a homonym and most people focused on the money.
Given that the cost to develop mediocre-to-competent level software has dropped to a $20 monthly subscription cost, it’s worth revisiting the other side of the free definition.
The principles behind wanting source code distributed with software are pretty reasonable.
Source code (and steps how to run it) is required if the user wants to repair a flaw. It’s also helpful if the user wants to alter the purpose from the original authors limited intent. Finally, it’s helpful for novices to learn by example.
If you’re here, you probably know what source code is, but let’s take a look at how it’s evolved over time.
The Ancient Ones
At its core, a computer is a yes/no machine. This yes/no, true/false, on/off (footnote: technically the correct one) lends itself to encoding as a base 2 number system. The CPU loads the instruction, then runs it.
The problem here is remembering if “139” was the addition instruction, or was it “2736”? Wait, it’s both, and they’re slightly different.
This is where assembly language came into play. Swap the hard to remember numbers with slightly less hard to remember characters. These characters are the mnemonics and the numbers they represent are the opcodes.
So instead of “139” you write ADD, instead of “2736” you write ADDS. And in both cases you open the manual to remember what the side effect of the second one was.
The program that took the mnemonics and their arguments (whatever you were adding) and translated it to the opcodes / binary was called an assembler, and this was Assembly language.
That 70s code
In the early 1970s, the C language was invented and by the late 1970s it was getting widespread use. There were other languages, but we don’t care about them in this narrative.
What C (and other compiled languages) did was provide a more compact way to express your programs.
// I wake up with zero limes int limeCounter = 0;
// I go to the store limeCounter += 5;
printf(“I have %d limes\n”, limeCounter);
This was a massive reduction in effort, since just the “I go to the store” section is this in assembly:
ldr w1, [x0] // load limeCounter add w1, w1, #5 // +5 str w1, [x0] // store back
[click here for the full assembly...]
.section DATA,data limeCounter: .long 0
.section TEXT,text .global _main
_main: // limeCounter += 5
adrp x0, limeCounter@PAGE add x0, x0, limeCounter@PAGEOFF
ldr w1, [x0] // load limeCounter add w1, w1, #5 // +5 str w1, [x0] // store back
// printf("I have %d limes\n", limeCounter)
adrp x0, fmt@PAGE add x0, x0, fmt@PAGEOFF
mov w1, w1 // second argument = limeCounter
bl _printf
mov w0, #0 ret
.section TEXT,cstring fmt: .asciz "I have %d limes\n"
The act of translating the C version to the assembly version is referred to as compiling. You can combine the compilation and assembly steps invisibly, but they are two separate stages.
Consistency not guaranteed
One subtle change introduced by leveraging compilers is that it leaves room for the translator to take some liberties.
In our example, the compiler chose to use the ADD mnemonic, but ADDS would have worked.
An observant reader will also notice that for this simple program, the variable was never used, so the addition operation can be removed, just start the lime counter at 5.
Compilers can, and do, take these liberties. Those are the compiler optimizations. That also means that different versions or brands of compilers generate different resulting outputs.
When someone expects the source code they mean the C version. That is closest to the intent of the author.
Open for interpretation
One downside to compiled languages is that the binaries they produce were tied to the specific CPU they are assembled for. You can write compiled code that could be cross platform but it takes more care and concentration.
In the late 1990s, while the Spice Girls were getting down with their friends, the Netscape team was hard at work. Inside of their web browser (Netscape’s, not the Spice Girls’) was an engine that could read uncompiled code hidden in the webpage that could modify the page itself.
Since the browser would be running on a whole bunch of different CPUs, it also had a different paradigm, the interpreter. Instead of transforming the language to assembly, then assembling, it converted the language to its own instruction set, then relayed that to the CPU via its own engine. (Or close enough)
This (JavaScript) was not the first interpreted language, but it may be the most prevalent. Since then, it’s been iterated on, extracted from the browser, and become one of the default languages preferred by LLMs, probably because of how commonly it’s used.
As the interpreters evolved, some created their own independent bytecode, but not in any common form.
Because there’s no distributable artifact, this meant that interpreted languages ship the source. There’s been effort to obfuscate the source for people to attempt to control their IP, but that comes at a trade off making diagnosing and fixing issues harder. At its core, the obfuscated code is still JavaScript, just a less legible variant. This will be important later.
Because the source code was right there in the web page, it drastically lowered the bar to entry for people looking to learn. And people did learn, probably one of the Top 8 times to learn ever.
our children will mock us too
So paradigms keep shifting and evolving. The weird thing about paradigm shifts is that they’re hard to qualify until they have landed and stabilized.
Let’s go back to the principles behind the Open Source movements. The spirit of it comes down to desires for a general practitioner to:
repair defects in the delivered software
learn from the original author
extend the functionality without reliance on the original author
What we are seeing more frequently though is the interface changing. A developer would author a set of instructions and the computer would run them as written.
Prompt-driven development either generates durable artifacts to be rerun, or the agent harness itself is treated as the interpreter. If the durable artifact is never being inspected, it’s materially no different than the machine code or the interim assembly.
So the source becomes that prompt, or series of prompts. Most people stop refining prompts when the output is good enough, since they never intended to share the process.
The output of the prompt becomes the JavaScript (or Rust, or Kotlin), but with all the intention stripped away. Nothing to learn from, closer to shipping the obfuscated JavaScript or assembly language versions and pretending that was the source code. The author themselves might not even understand the intermediate representation. But “shipping the prompt” is a term almost derisively used, since it’s so unreliable.
One thing programming languages don’t support is ambiguity. If English language is a form of compression, it’s a lossy one. English thrives in ambiguity, the word “should” is a pox on all requirement documents. So the prompt alone is nowhere near good enough.
So I’m not advocating for “shipping prompts” in isolation, but what was source code six months ago is losing value. Do we invent something new?
Knuth knew
One thing to keep in mind is that for each revelation, it wasn’t cutting edge technology, it was something ahead of its time, waiting patiently for wider industry to catch up.
Our compiler examples took place in 1975, but the concept had been around since 1957. Interpreters were conceived of only one year later, in 1958, but the mainstream was catching up in 1995.
If we look at the shape of the problem I’ve been describing, it bears a strange resemblance to Literate Programming, introduced by Donald Knuth, in 1984. The programmer authors an essay with code embedded in it, then leverages a pair of tools. One tool (tangle) optimizes output for the compiler, the mirror (weave) optimizes it for the reader. The computer consumes the instructions, we consume the intent. Instead of comments littering a codebase, you would ship an essay, with code to resolve ambiguity. In Literate Programming, the prose is the program. In today’s world, the prompt, or some derivation of it, seems important to retain. Especially so for Open Source. If the intent is lost, the “source” code is not much better than shipping the assembly, or obfuscated JavaScript.
It stayed a curiosity in some academic circles. Jupyter notebooks got somewhat close, but in practice they tend to just be a string of chained cells. Knuth’s solution might not have seen much adoption, but he definitely saw the problem coming, waiting over 40 years for the rest of us to catch up.
I’m not sure what the correct play is here, but this song sounds very familiar. I don’t know all the words, but I think I can at least hum the tune.