W E L C O M E   T O
                                 .___     .__
_____ _______   ____ _____     __| _/____ |  | ___.__.
\__  \\_  __ \_/ ___\\__  \   / __ |/ __ \|  |<   |  |
 / __ \|  | \/\  \___ / __ \_/ /_/ \  ___/|  |_\___  |
(____  /__|    \___  >____  /\____ |\___  >____/ ____|
     \/            \/     \/      \/    \/     \/
-- Writing, games & side quests - Bart's small website --
            *      .         .
    .
                      *
        *        *
                    .
             _\/_
       _\/_  /o\\
'-,    //o\   |
   ,-_,-|-""'"|'-,_
        |       "  "-,_
                  "    "--.
          *         .
  .            .-.
              ( (       *
        *      '-'
                  *
          _\/_
          //o\  _\/_
            |   /o\\    ,-'
        _,-'|"'""-|-,_-,
    _,-"  "       |
.--"    "

Agentic Software Delivery & The End Of Source Code As We Know It

Last night I went out for drinks and pizza with colleagues. As we were chatting away one person said to the group that the logical conclusion of progress in agentic development, with direct human interaction with code ever decreasing, is the complete elimination of source code. Instead agents would work directly with binary machine-readable forms because, after all, if the code will never be read by humans, why would it need to be human-readable?

A stack of seven layers headed "Which layer should agents author in?". From top to bottom: specs, constraints and prompts; SQL, DSLs, config, markup and templating languages; Java, C#, Python, TypeScript, TSX, F# and Clojure; C, C++, Rust and Zig; JVM bytecode, .NET CIL, WebAssembly and LLVM IR, drawn dashed as a machine-oriented intermediate layer; assembly, noted as used for embedded systems, some kernel and driver code, boot code and hot loops; and machine code and binary, beside which is the question "Should agents operate here directly?". A bracket marks the three layers from SQL down to C, C++, Rust and Zig as where most code was written before LLMs. An arrow runs down the left side of the stack, labelled "compiled, interpreted or JIT'd downwards".
An imperfect, and in some aspects debatable, view of the hierarchy of abstraction layers at which programming languages operate. Much progress in programming languages over the decades has been at higher levels of abstraction that have helped software engineers become more productive. However, when the use case justifies it, we still use languages at lower levels of abstraction. So the question is, in the era of AI, LLMs, and agents, do we still need those higher levels or will agents work more effectively if they operate directly at the binary or machine code level?

I’ve heard similar things before and they’ve never sat quite right with me. But, for various reasons, I’ve never given them much serious thought. Last night was no exception: I was several drinks in, feeling relaxed and happy, and didn’t feel like getting into an argument that I couldn’t even articulate beyond a vague sense of unease with the position. And so the conversation quickly moved on.

Today I’m sat on a train, heading home, for five hours. I’ve been re-reading The Mythical Man Month by Fred Brooks to pass the time. In doing so I’ve just encountered the reason for my unvoiced discomfort printed plain as day in the midst of chapter 16:

What does a high-level language accomplish? It frees a program from much of its accidental complexity. An abstract program consists of conceptual constructs: operations, datatypes, sequences, and communication. The concrete machine program is concerned with bits, registers, conditions, branches, channels, disks, and such. To the extent that the high-level language embodies the constructs wanted in the abstract program and avoids all lower ones, it eliminates a whole level of complexity that was never inherent in the program at all.

It’s that “whole level of complexity”: if we fail to eliminate it from the input to our agents, how is the cost counted?

Unless there’s a compelling reason to, reverting to a lower level of abstraction from a higher level and taking on the complexity that goes with that - feeding all of that additional information into our agent - pollutes our reasoning space. It literally enlarges the state space that reasoning has to operate within. In doing so it increases the risk of hallucination and lowers the quality and relevance of generated output because all of the additional irrelevant information competes for attention with the details and concepts that are directly relevant to the problem we’re solving.

For example, if I want an agent to change rules governing invoice approval for a certain class of supplier, what I want it to think about is approval rules, invariants, permissions, business rules and outcomes: I don’t want it to think about register allocation, memory layout, or the mechanics of marshalling and parsing HTTP requests and responses. The latter are all things that might need to happen to enable the former, but they’re not relevant to solving the business problem I have: to modify my invoice approval process to meet some new business requirement.

The same invoice approval rule - class B suppliers need two approvers for invoices over £10,000 - shown three ways, with the lines that express the rule highlighted. Each version also handles invoice submission. As a declarative rule, a short submission action sits above the rule, and the six lines of the rule itself are highlighted. In Python, the rule is one highlighted function beneath an HTTP handler, so it seems like a really good fit for an abstraction level that's well matched to the problem domain. In x86-64 assembly, seven highlighted instructions are lost among around fifty lines of stack management, HTTP header and JSON parsing, and repository calls, none of which are directly relevant to the rule - there's too much extraneous detail.
The same simplified invoice approval logic at three levels of abstraction, with the lines that actually express the rule highlighted. In this isolated example it seems like Python is a good match for the level of abstraction we need to work with our business rules, whereas the x86-64 code brings in all manner of extraneous detail relating to stack management and a bunch of other stuff that, whilst necessary, we don't care about so much for the problems we're considering.

If I force the agent to work at a lower level and ingest lots of extra unnecessary detail, that costs more in terms of reasoning, tokens (or their future equivalent), and time. It causes false starts, mistakes, and dead ends. Reasoning is degraded. Worse, it results in additional compaction cycles which themselves risk losing relevant information, thus accelerating reasoning degradation. That degradation translates into both financial and compute costs that are simply wasted.

As Fred Brooks pointed out in the quote above, abstracting to a higher level is beneficial because it constrains what needs to be considered. It removes all the unnecessary lower-level concepts and their details and instead allows us to focus on the core concepts and details of the problem we’re trying to solve.

That’s just as relevant for LLMs and AI agents as it is for humans.

Removing the lower-level detail cuts the complexity with which reasoning has to contend. That leads to better reasoning and higher-quality code generation. In an AI-SDLC1 that will contribute to improved downstream reasoning and interventions, which in turn will translate into improved quality in the delivered software.

I’d argue that agentic development rewards abstraction and increases the value of patterns and practices that we’ve historically treated as aids to our own understanding and reasoning:

These constrain and communicate intent to whoever or whatever is working on our system, whether that’s a human being or an agent.

AI doesn’t make software engineering abstractions obsolete. Instead, it shows they do more than simply overcome human limitations in understanding and cognition. Agents will be more productive using language representations and tooling at a level of abstraction that matches the problems and domains they’re working in. For many use cases that will be a higher level. This is very similar to the situation for humans.

What might happen with time is that we – or perhaps AI agents under our direction – will develop new high-level languages, along with perhaps shorthand symbolic forms, that express intent in a way that works better with those agents. That I could certainly see.

4GL ideas have long since been incorporated into programming languages that are popular today, like Python and Ruby, and that will no doubt continue. What I think is more interesting is that the field has been opened up for not just the development but the day-to-day realisation of 5GL ideas. Arguably agentic development is itself a 5GL manifestation. And much of today’s discussion and research on AI-SDLC and AI governance touches on or incorporates 5GL ideas. For example, it’s not hard to see the connection between the concept of acceptance criteria and the idea of programming by constraint. Taking things further, we might start to see higher-level representations that don’t follow the typical linear, bloated, syntax-heavy, explicit nature of many programming languages so far developed for human use. Instead we might develop alternative high-level representations that are more suitable for ingestion, reasoning, and transformation by agents. These might be constraint-based, declarative, graph-like, relational, or specification-heavy.

None of this precludes representations that are also human-readable2, or human-readable projections of those representations. But the point is that we might be able to move to higher-level representations of “code” than we’ve previously been able to make effective use of, and could see rapid progress and evolution in language development to enable more effective use by agents.

Obviously this won’t impact all domains or scenarios equally: there will remain cases where lower-level languages still have relevance, such as in operating systems and embedded development. That will continue to be true regardless of whether humans or agents are writing the code.

There are also plenty of reasons why human readability will remain an important facet of source code, especially in verticals like banking, healthcare, aviation, government, defence, and infrastructure. These are all heavily regulated, and systems developed within them are often highly consequential - incorrect operation can result in financial ruin, legal jeopardy, or sometimes even physical disaster.

If humans and human-administered organisations are going to take responsibility for software and be held accountable for the consequences of its misbehaviour, then humans are going to need to be able to inspect its source representation (or a projection of it): to investigate, to understand, to audit, to govern, and to regulate.

Nobody will accept “the agent gets it” as final assurance.

Software also encodes organisational knowledge in its source code. Its version history, along with the latest snapshot, constitutes institutional memory. Software contains the results of countless decisions made about the business it operates in, the domain it models, and the wider world that it integrates with. For example, a customer cannot transfer more than X between accounts in a single transaction without triggering AML procedures. Software is an executable specification that models and reflects some part of the reality in which it runs.

We worry about loss of organisational knowledge, which can happen for many reasons: staff turnover, data loss (including loss of source code), changing supplier relationships, unforeseen disaster, or a combination of factors. Many organisations take steps to prevent such loss. Still, many of us have worked with organisations that have lost knowledge of some part of their own systems and operations, and have helped them reconstitute that knowledge. At a very basic level, when I worked on .NET Reflector I helped several customers use it during source code reconstructions for systems where the original source code had been lost - and you’ll no doubt recognise that decompilation is only the first step on that journey. Full recovery of lost knowledge is challenging and expensive work. So there’s a strong argument against making institutional knowledge inaccessible by rendering it illegible to humans simply to benefit machines: I think we can create authoritative source representations that benefit both.

However, we should avoid assuming that there’s a 1:1 correspondence between human readability and agentic tractability. It’s quite possible there will be agent-friendly representations that work incredibly well for the agents but are a real headache for us humans. In some situations it’s possible the benefits of tolerating those headaches will outweigh the costs. We’ll have to find ways to deal with that: perhaps with some form of projection, as I’ve already suggested, even if that’s not the canonical source of truth but “merely” a presentation layer for our benefit. Possibly that’s OK as long as we retain the ability for humans to inspect and understand the authoritative representation of the system.

A two-by-two grid with human readability on the vertical axis and agentic tractability on the horizontal axis. High on both is the sweet spot: high-level languages, declarative specs, meaningful names and strong interfaces. Readable but hard for agents to act on: prose requirements, and intent buried in boilerplate. Low on both: hex dumps, obfuscated binaries and Brainfuck. Tractable for agents but hard for humans, drawn dashed because it doesn't exist yet: agent-native forms such as dense graphs and symbolic shorthand.
Human readability and agentic tractability aren't necessarily the same thing, so it's not a given that what works well for humans will also work well for agents, and vice versa. But there is likely to be a sweet spot that offers benefits to both that make the trade-offs worthwhile. I suspect that sweet spot may not be so far removed from the kind of high level engineering artefacts we work with today.

I confess to a certain amount of scepticism here. I think we’ll prefer to avoid the machine-readable form as the source of truth because it could leave us having to reverse-engineer it if we find we can no longer trust that the human-readable projection represents the system accurately. I think it’s likelier that we’ll find more efficient ways to present high-level human-readable forms to agents, but that the human-readable form will remain authoritative. Tools such as compilers will continue to operate on the human-readable forms, thus preserving those forms as the source of truth.

So my overall conclusion is this: as with human software development, agentic software development argues for operating at a level of abstraction that matches the problem domain. For most software delivery scenarios, this will be at a higher level, rather than a regression to the lower-level abstractions that we worked with in the past when we had little other choice. Moreover, the need for authoritative human-readable representations of systems and their behaviour remains as strong as ever.

Footnotes

  1. 1

    AI-driven Software Development Lifecycle (sometimes AI-native or AI-powered Software Development Lifecycle, or other similar variants), abbreviated to AI-SDLC, is still an emerging and immature field - so much so that even the meaning of the acronym hasn’t fully coalesced yet.

    What this means is that whilst there are plenty of good resources available online, they tend to be on company websites and therefore push a version of AI-SDLC framed from the point of view of a particular organisation. They’re still valuable, but it’s obviously a good idea to draw from multiple sources. Here are a few that I’ve found useful:

  2. 2

    I’m perhaps being slightly loose in my terminology when I say “human-readable”. A human can read x86 or 6502 assembler (or even a stream of hex op codes if they’ve been doing it long enough); SQL is human-readable, JSON is human-readable, a dump of an AST is human-readable (relevant from when I worked on SQL Prompt), Brainfuck is technically human-readable. But they’re not all equally pleasant or easy to read or reason about. When I say human-readable, what I mean is that a human can understand it at an appropriate level of abstraction. Going back to our invoice approval example, we’ll find that code a lot easier to understand and reason about if it’s written in Python than we will if it’s written in x86 assembler.

Filed Under

Category:AI

Tags:ai · llms · agentic · ai-sdlc · agents · software delivery · agentic software delivery · programming languages · code · abstraction

I'd love to hear your views.

You can drop me a line, or write your own post and send me a link. Anything interesting I receive I'll add here.