{"slug": "source-code-is-becoming-a-build-artifact", "title": "Source Code Is Becoming a Build Artifact", "summary": "AI is shifting software engineering from hand-written source code to requirements-driven generation, making source code a build artifact produced by tools. This transition mirrors the shift from assembly to high-level languages like FORTRAN, where compiler quality was the key to acceptance. The challenge for AI-generated code is ensuring it can be trusted without relying on the generator's own assessment.", "body_md": "# Source Code Is Becoming a Build Artifact\n\nEngineers have programmed systems through physical configuration, assembly code and languages such as C, C++ and Rust. Each higher layer delegated more translation to tools. In disciplined embedded development, requirements already guide the human implementation.\n\nAI may now change which layer engineers author directly: precise requirements, controlled design inputs and acceptance criteria can become the reproducible basis for generated code - but only if the result can be accepted without trusting the generator's own verdict.\n\nThere is a pattern in the history of software that becomes easy to overlook once a new abstraction has become normal. Engineers move the point at which they describe a system. The lower layer does not disappear; it becomes something a tool produces for us, and we go back to it only when we have a reason.\n\nThese examples overlap historically. They are arranged by abstraction level: the point at which a human describes what the machine should do.\n\nAt the lowest level, that description can be physical. Connections, switches, coefficients and machine configuration embody the computation directly. At the next level, symbolic instructions describe machine operations. With high-level languages, engineers still implement against requirements, but they express the solution as algorithms, control flow and data structures while the compiler translates it into machine instructions. AI-based development may delegate one more transformation: from controlled engineering intent to candidate source code and tests. Requirements are not new; what changes is how directly they drive the implementation.\n\nEarly computing makes the abstraction boundary unusually visible. In a machine like this analog computer, the intended calculation is expressed directly in the physical configuration of the machine: connections, functional blocks and parameters become part of the program.\n\nThe machine in the photograph dates from 1968, long after stored-program computers and FORTRAN already existed. It illustrates coexistence, not sequence: lower and higher abstraction levels remained in use at the same time. What changed was the level at which more engineers could describe the result they wanted and delegate the translation to another layer.\n\n## We have made this move before\n\nThe more important transition was not from one particular machine to the next, but from one level of description to another. High-level languages made that shift consequential: programmers could express algorithms in a notation designed for humans while a compiler took responsibility for translating them into machine instructions.\n\nFORTRAN moved that boundary dramatically. Programmers still implemented required behavior, but they no longer had to express every operation in the vocabulary of the processor. The quality of the compiler’s translation - especially the efficiency of the generated code - became the central engineering question.\n\nWhen John Backus and his team developed FORTRAN in the 1950s, programmers were skeptical of automatic programming, and for good reason. Skilled programmers knew their machines intimately and could produce very efficient hand-written code. A translator that produced poor code would not have been useful simply because it made programming more convenient.\n\nBackus later wrote that the FORTRAN team believed their system would only be accepted if the generated programs came close to the efficiency of hand-coded ones. The compiler was therefore not an afterthought; it was the central engineering problem. Once that problem was solved well enough, the level at which programmers worked changed surprisingly quickly.\n\nLooking back, it is tempting to describe that transition as the replacement of one skill by another. I think that misses the more important point. The programmer still had to understand the problem, the machine and the consequences of the implementation. What changed was the level at which the solution was normally expressed.\n\nAssembly code did not vanish when C arrived, and machine code did not vanish when assembly code arrived. Both moved down the stack. We still inspect them when timing is tight, when a compiler does something unexpected or when the hardware behaves differently from what the source suggests. They remain important without remaining the place where every development starts.\n\nThe important sequence is not chronological. It is functional. We are not looking at generations of machines, but at generations of abstraction between human intent and machine execution.\n\n## Now source code may be moving down the stack\n\nRequirements are not new. In a disciplined embedded project, they already define the behavior to be implemented and accepted. Today, a human engineer interprets them - together with architecture, interfaces and other controlled design inputs - and writes C, C++ or Rust. The compiler turns that source into object code, and the target executes it.\n\nAI-based development changes the translation step, not the existence of the requirements. A generator can produce candidate code, tests and configuration from sufficiently precise requirements and acceptance criteria. The engineer still owns the intent; the compiler still produces the executable; and a separate acceptance mechanism still has to establish whether the resulting binary did what was required.\n\nThe source language did not make the machine code unimportant. It changed who was expected to produce it.\n\nAI-based development may do something similar to source code. C, C++ or Rust remain important artifacts. Engineers still need to inspect and debug them. But increasingly they can be generated and regenerated from a higher-level description of intent.\n\nThat is why source code is becoming a build artifact. Requirements and acceptance criteria have long been durable engineering assets. What changes is their operational role: together with controlled design inputs, they can become the reproducible basis from which candidate code and tests are generated, while evidence records whether the resulting binary satisfied them.\n\nIf that is the new development stack, the difficult part moves as well. Generating another implementation becomes cheap; deciding whether that implementation can be accepted does not.\n\nThat chain contains three different engineering layers that are often grouped together.\n\nA hardware-aware generator can read technical documentation, produce firmware,\nflash a board and run functional tests. A standards-aware generator can produce\ncode aligned with rules such as MISRA® or AUTOSAR. Both are useful advances.\nNeither one, by itself, is the release decision.\n\nAcceptance asks a different question: did the optimized target binary satisfy the declared acceptance criteria, measured through a mechanism whose criteria, oracles and verdict the generator cannot control - and with anything not established left visible?\n\nCEDARtools.Forge is built around that third layer. Generation produces candidates. A separately controlled acceptance mechanism decides what the available evidence actually establishes.\n\nA build artifact can be extremely important and still be replaceable. We care deeply about the binary that runs in a controller, but we do not normally edit that binary by hand. If it is wrong, we fix the source and build it again. In the same way, if generated C can be reproduced from an authoritative requirement, then the long-term asset is no longer every individual line in the generated file.\n\nThis also gives us a useful test for whether the abstraction is complete. If engineers routinely have to hand-edit generated C and preserve those edits because the higher-level description cannot express them, then the C file is still partly source. The day those changes can be expressed in the controlled higher-level inputs - requirements, architecture, interfaces, constraints or configuration - and the code can be regenerated without losing intent is the day the role has genuinely changed.\n\n## A prompt is not the new source language\n\nThere is an obvious trap here. If requirements and controlled design inputs are to become a direct generation basis for source code, that does not mean ordinary prose suddenly becomes an executable specification. A prompt such as “build a safe motor controller” leaves almost everything that matters unanswered. A human programmer would have to make assumptions, and an AI generator will do the same.\n\nThe useful abstraction is therefore not natural language by itself. It is intent that is precise enough to be checked. Depending on the system, that may combine normal prose with interface definitions, timing limits, state models, invariants, examples and acceptance criteria. The exact notation matters less than whether two engineers can read the requirement and agree on what observable behavior would satisfy it.\n\nIn that sense, AI makes good requirements more important rather than less important. A strong generator can turn ambiguity into a plausible implementation very quickly. That is helpful when the ambiguity is harmless, but dangerous when the missing decision concerns a failure mode, timing constraint or boundary condition. The better generation becomes, the less comfort we should take from code that merely looks reasonable.\n\nDomain rules improve the candidate, but they do not replace acceptance. Code can be MISRA-clean, AUTOSAR-aligned or generated from a hardware-aware context and still implement the wrong requirement, omit a boundary condition or leave behavior unexamined in the optimized binary. Conformance by construction reduces known classes of defects. Independent acceptance establishes what has actually been demonstrated for this particular build.\n\n## The compiler analogy has a limit\n\nA compiler and a generative model are not the same kind of tool. Given the same source, compiler version, options and target, a compiler will normally perform the same transformation again. That repeatability is valuable, but it should not be confused with infallibility. Conventional compilers are complex programs and can contain defects, including defects that silently produce wrong code.\n\n[CompCert](https://compcert.org/compcert-C.html) is a notable exception to the\nusual level of assurance. Its central compilation passes are accompanied by\nmachine-checked proofs that the generated assembly code preserves the behavior\ndefined by the source program. The qualification is important: not every step\naround the compiler lies inside that proof boundary. Preprocessing, parts of\nthe front end, assembling and linking still have to be considered separately.\nCompCert therefore does not make verification unnecessary; it shows how much\nconfidence can be gained when the translation itself is no longer treated as an\nopaque implementation detail.\n\nGenerative AI starts from a less stable position. The same requirement can lead to different implementations, and gaps in the requirement may be filled with assumptions that are plausible rather than intended. Reading the generated source helps, but it is not enough to establish trust. If the chain from requirements through AI to code is to be dependable, every generated candidate has to be evaluated through an acceptance mechanism whose criteria, test oracles, measurement path and verdict are controlled outside the generator. The generator may propose artifacts, including tests; it must not be the sole authority over what counts as a pass.\n\nSo what does that evidence have to look like? Generated source code is not literally a black box: the code is visible, can be reviewed and can be debugged. What remains opaque is the relationship between that code and the engineer’s original intent. Reading the implementation cannot tell us with sufficient confidence that every requirement was implemented, that no unintended behavior was introduced, or that all relevant behavior will occur correctly on the target. Trust therefore has to come from evidence collected independently of the generator’s verdict. Independence here does not mean that no generated artifact may be used. It means that acceptance-policy ownership is separated from artifact generation, that tests and oracles are versioned and reviewable, and that the generator cannot silently alter the conditions under which its own output is accepted. Within that arrangement, the evidence itself needs two kinds of completeness.\n\n### What acceptance evidence has to establish\n\nThe first is functional completeness. Every declared acceptance criterion in scope needs a recorded verification outcome. Where the claim concerns runtime behavior, tests include relevant boundaries, error cases and failure behavior and run in an environment representative of that claim. This is the minimum, and on its own it proves less than it seems: requirements-based verification says nothing about implementation behavior that no requirement asked for.\n\nThe second is structural completeness. Structural coverage does not prove that the implementation is correct, but it reveals which parts of the generated implementation were not exercised by the requirements-based tests. That makes the remaining gaps visible and turns them into explicit engineering questions rather than hidden behavior.\n\nWe measure coverage top-down: start at the highest integration level at which the requirement can be meaningfully observed, then use integration and unit tests for obligations that demonstrably remain. Each step down the ladder is justified, and so is whatever remains unexecuted at the bottom: a missing requirement, which gets written; a missing test, which gets added; or unreachable code, which is either removed or - where it exists intentionally, for example as a defensive check or deactivated feature - retained with a documented justification.\n\nThe accounting is complete only when every structural item is either exercised\nor explicitly dispositioned. A\njustification does not turn an unexecuted item into covered code. It records\nwhy the item remains outside executed coverage, the evidence supporting that\ndecision and who approved it. That is the method we laid out at\n[DX 2024](https://doi.org/10.4230/OASIcs.DX.2024.19) and\n[Embedded World 2026](https://arxiv.org/abs/2608.13322), and it is the only\nsense in which we use the word complete.\n\nWhere the agreed assurance scope calls for target evidence, execution and coverage are collected on the target or project-representative hardware from the optimized binary under review. This avoids relying solely on a model for the observed behavior. Virtual platforms may still be useful elsewhere in the lifecycle, but representativeness has to be argued wherever credit is taken for their results.\n\nThe application executes on the target while trace-based observation avoids application-side coverage counters in the binary under review. The supported trace configuration, measurement boundary and applicable probe effect are stated for the target and engagement.\n\nIs that more effort than most projects spend today? Yes, and by a wide margin if people had to write and run those tests by hand. The objection weakens when the tests are generated and executed by machine against requirements the engineer owns and the structural accounting comes from the observation described above.\n\nOnly then is the generated implementation trusted for a reason that can be written down: every declared acceptance criterion in the agreed scope has a recorded outcome, every structural item in the agreed scope was either exercised or explicitly dispositioned, with executed coverage and justified non-execution reported separately, and the measurement was taken on the target, not in the generator’s own account of itself. The result is a stronger and more reviewable assurance case - not proof of complete system correctness, but a recorded basis for understanding what was tested, what was observed and what remains outside the claim.\n\nWithout that evidence, the process is still trusting a black box. The generated code may be visible and readable, yet the decision to accept it would rest on the model’s apparent plausibility rather than on a checkable engineering case. The objective is not to prove that AI is generally trustworthy. It is to make each generated implementation earn trust by showing what it does, what was tested and how the results relate back to the requirement.\n\n## CEDARtools.Forge closes the acceptance loop\n\nAI coding tools no longer necessarily stop at source code. Hardware-aware agents can compile, flash, drive bench instruments, run their own tests and repair the result on real silicon. That closes a functional generation loop. It does not by itself close an acceptance loop, because the same system may still propose the implementation, propose much of the test logic and interpret the result.\n\nCEDARtools.Forge places a separately controlled acceptance policy around the candidate-generation loop. The engineer owns the requirements and acceptance criteria. Within the workflow, a generator may produce code, tests and configuration; deterministic build, test, coverage and integration gates evaluate the result and produce pass, fail or unresolved outcomes from recorded inputs. The generator may iterate, but it cannot change what counts as a pass or set its own result to green.\n\nThe candidate executes on the target, and Forge* combines the functional results with structural coverage derived from the target’s trace data. Anything left uncovered has to lead somewhere explicit: to a missing requirement, a missing test, unreachable code that should be removed, or a documented justification for code that intentionally remains.\n\nThis creates a closed acceptance loop. When a requirement changes, the implementation and its tests can be regenerated and the evidence can be rebuilt from execution on the target, without making the engineering decision less reviewable.\n\nThere is also an economic consequence. Generating tests from requirements and collecting the structural accounting from trace-based observation of the binary under review can make this discipline economically plausible for a broader class of projects than those that already budget for certification-driven evidence.\n\nThis is the important difference between code generation and closed-loop development. Generating software is becoming easy. Forge* is intended to make it practical to decide, with evidence rather than plausibility, whether the generated software should be accepted.\n\n## What changes for engineers?\n\nSome low-level skills will become less common, just as many software engineers today rarely write assembly code. That is not necessarily a loss of engineering competence. It is a shift in where that competence is used.\n\nSomeone still has to notice that two requirements contradict each other, that a specified timing budget cannot be met on the selected hardware, or that a failure case has never been defined. Someone still has to decide whether a trace from the target actually supports the claim being made. Those tasks become more important when generating another candidate implementation becomes cheap.\n\nThere will also be plenty of reasons to go down a layer. When a compiler behaves unexpectedly, we inspect assembly code. When generated C behaves unexpectedly, we will inspect C. The existence of a higher abstraction does not remove the lower ones; it changes how often we have to work there and what we expect to preserve by hand.\n\n## One more layer up\n\nThe progression from plugboards to assembly code and from assembly code to C, C++ and Rust did not remove engineering from the process. It changed the level at which engineers normally expressed their intent and allowed increasingly complex systems to be built without manually carrying every lower-level detail.\n\nThere is no obvious reason to assume that today’s programming languages are the final layer engineers must author directly. Requirements and acceptance criteria already guide disciplined development. They may now become the controlled, reproducible basis from which candidate implementations are routinely generated - provided they are precise enough and the result can be accepted independently of the system that generated it.\n\nThat is the condition that matters. If changing a requirement allows us to regenerate the implementation, execute it on the real target and rebuild a reviewable chain of evidence from the requirement to the observed behavior, then source code no longer has to carry the project’s intent by itself.\n\nAI can now write firmware that runs. Hardware-aware agents can even flash the board and run bench tests. Standards-aware models can produce rule-aligned code. CEDARtools.Forge answers the next question: did the optimized target binary satisfy the declared acceptance criteria - measured through a mechanism other than the generator, with everything not established left visible?\n\nOnce that loop is complete, source code has not disappeared. It has simply taken the same step that machine code and assembly code took before it: still important, still inspectable and still available whenever engineers need to go down a layer, but no longer necessarily the primary artifact they author and preserve as the expression of system intent.\n\n[Explore CEDARtools.Forge](/forge/) ·\n[See the CEDARtools.Forge assurance boundary](/forge/assurance-boundary/)\n\n## Historical notes\n\nFor the history of FORTRAN and the skepticism around automatic programming,\nsee John Backus, *The History of FORTRAN I, II, and III*, ACM SIGPLAN Notices\n13(8), 1978, DOI\n[10.1145/960118.808380](https://doi.org/10.1145/960118.808380), and\n[IBM’s historical material on FORTRAN](https://www.ibm.com/history/fortran).\n\nFor compiler correctness, see Xuejun Yang et al.,\n[ Finding and Understanding Bugs in C Compilers](https://www.flux.utah.edu/paper/yang-pldi11)\n(PLDI 2011), and the\n\n[CompCert C compiler](https://compcert.org/compcert-C.html).\n\nImage credits: the EAI 580 plugboard photograph by Tomwsulcer is released under CC0 via [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:EAI_580_analog_computer_plugboard_at_CHM.jpg); the Motorola 6800 listing (Michael Holley) and the FORTRAN punch card (Tangopaso) are in the public domain.\n\n**Related:** [CEDARtools.Forge](/forge/) ·\n[the CEDARtools.Forge assurance boundary](/forge/assurance-boundary/) ·\n[closed-loop AI-based development](/technology/closed-loop-ai-development/) ·\n[coverage for integration evidence](/products/coverage/integration-evidence/) ·\n[our publications](/evidence/publications/)\n\n**Comments or feedback on this article?**\n[blog_comments@accemic.com](mailto:blog_comments@accemic.com)", "url": "https://wpnews.pro/news/source-code-is-becoming-a-build-artifact", "canonical_source": "https://accemic.com/articles/source-code-is-becoming-a-build-artifact/", "published_at": "2026-08-21 10:22:20+00:00", "updated_at": "2026-08-21 10:44:12.333617+00:00", "lang": "en", "topics": ["artificial-intelligence", "generative-ai", "developer-tools"], "entities": ["John Backus", "FORTRAN", "C", "C++", "Rust"], "alternates": {"html": "https://wpnews.pro/news/source-code-is-becoming-a-build-artifact", "markdown": "https://wpnews.pro/news/source-code-is-becoming-a-build-artifact.md", "text": "https://wpnews.pro/news/source-code-is-becoming-a-build-artifact.txt", "jsonld": "https://wpnews.pro/news/source-code-is-becoming-a-build-artifact.jsonld"}}