What Code Really Is

Ask a developer what code is and the answer sounds straightforward: instructions for a machine, written in a programming language. For decades, that definition was enough. Producing code meant typing it out, compiling it, testing it, and shipping it. The effort involved in writing each line gave code its value.

Generative AI has changed that equation. Large language models can generate executable code from high-level descriptions, turning the mechanical act of writing code into a commodity. That shift forces a more fundamental question: If producing code becomes cheap, what is left that is actually valuable?

Code has always served two intertwined purposes. One is mundane: it directs computation, moves data, and coordinates execution. That part is being commoditized by LLMs. The other purpose is deeper. Code is a conceptual model of the problem domain—a representation of the vocabulary, rules, and boundaries that humans use to reason about a system. The activity we call coding is the point where these two purposes meet, shaping the concepts through which the system is understood.

Vocabulary as the Substance of Design

Every serious domain carries its own vocabulary. Banking, retail, healthcare, and insurance all have established terms with specific meanings. Software development itself is a domain, with words like abstraction carrying connotations that go beyond their plain-English usage. Communication—whether with a person, a framework, or an LLM—depends on shared vocabulary. Words must map to concepts the receiver can act upon.

Code sits at the intersection of multiple domains. A retail system is not just about products and shipments; it is also about web semantics, HTTP methods, and caching behavior. Someone building that system must be fluent in both worlds. Consider a retail domain: a catalog can be modeled as a web resource supporting GET, POST, PUT, and DELETE. The developer is translating retail concepts into technical concepts, building a new vocabulary along the way.

A well-designed codebase makes that vocabulary visible. Concepts become types, relationships become interfaces, rules become invariants, and workflows become compositions. This is not a top-down exercise. The right names for variables, the boundaries of methods, and the hierarchy of classes emerge through iteration. Refactoring against real-world constraints slowly turns code into a readable, specific representation of the domain itself.

For stable, recurring technical patterns, frameworks and libraries act as codified vocabularies. They capture common usage patterns, which is why ecosystems like the Spring Framework exist for enterprise applications involving web and integration concerns. Each programming language brings its own flavor, with design constraints that get encoded in its libraries.

The Limits of Generic Frameworks

But not every domain has stable enough semantics for a universal framework. A stock exchange or an online retail business is not just a technical stack. Attempts to create universal abstractions for such domains either become too generic to be useful or too opinionated to be broadly applicable. The closer you get to the core business model, the more the vocabulary must be discovered locally.

This is where the concept of a bounded context from Domain-Driven Design becomes essential. A bounded context marks the boundary within which a particular model and vocabulary are valid. The same word can mean different things in different contexts, requiring distinct abstractions and rules.

Building these local vocabularies takes collaboration. Techniques like test-driven development are strong tools for iteratively discovering the right abstractions, because they force continuous feedback between the model and its behavior. Coding cannot happen in isolation—it requires close collaboration with domain experts and users. The agile emphasis on customer collaboration and responding to change is not process ceremony; it is a mechanism for refining vocabulary through feedback. Domain-Driven Design makes this explicit through the ubiquity of a shared language between developers and domain experts, tested continuously against working software.

Programming Languages as Thinking Tools

Building vocabulary through code requires active engagement in writing and reshaping it, not just passively reviewing generated output. Deep thinking about code often happens only when you are actively typing it. The constructs and constraints of a programming language become thinking tools. The channels and lightweight threads of Go, the object-oriented model of Java, and the ownership model of Rust each push thinking in particular directions. Programming languages do not just express a design; they help you discover it.

         var future1 = action1();
         future1.thenCompose(val1 -> action2(val1))
                .thenCompose(val2 -> action3(val2))
        

This becomes apparent when working on custom implementations. Designing a Future API for asynchronous examples requires understanding functional programming concepts to express sequences of actions cleanly. Without those concepts, the implementation and its usage become awkward.

Sometimes the language itself hides the structure of a solution. Describing the essential requirements of a snapshot isolation implementation in plain English proved vague; expressing it in Java code was too verbose. A single page of a pseudo-formal specification, closer to TLA+, was far more effective.

         Begin(T, coord):
           R(T) := HLC(coord).now()
           writeSet(T) := {}

         Read(T, N, key):
             N.HLC.tick(R(T)) //HLC advanced. So any write or commit after this is guaranteed to be at a higher ts
             return latest committed version of key with ts <= R(T)

         Write(T, N, key, value):
             N.HLC.tick(R(T))
             if LatestCommittedVersion(key).ts > R(T):
                 abort T
             place provisional intent for (key, value)
             writeSet(T) := writeSet(T) union {key}
      

That exercise clarified the thinking and served as a solid foundation for discussions, implementation, and validation through tests.

The Role of LLMs

Seen through this lens of code as vocabulary, LLM-assisted development takes on a different character. LLMs are trained on the vocabulary of huge amounts of code and text, learning the relationships between names, APIs, frameworks, and patterns. Words like Controller, Repository, Reducer, or TransactionLog carry known structural associations. This is why precision matters in prompts and in the codebase. Vague language forces the model to guess intent; a consistent, well-named codebase gives it stable structure to follow, letting it map intent to a useful implementation.

The Danger of Cognitive Debt

This also explains a specific hazard of LLM-assisted coding: cognitive debt. It accumulates when words, abstractions, or structures are used without the people involved understanding their deeper meaning. LLMs can amplify this risk dramatically, generating plausible code full of familiar design patterns that compiles and maybe passes tests, yet embodies a conceptual model no one on the team actually owns. The issue is not that an LLM wrote the code; it is that the code introduced new vocabulary faster than the developers could build an understanding of it. That gap between generated structure and human comprehension is a primary source of cognitive debt.

The Role of Abstractions in an LLM-Driven Workflow

Writing code involves two intertwined activities: discovering abstractions and then applying them. The discovery phase is about developing a vocabulary. Once that vocabulary is built, it represents a shared conceptual model. Much of coding then becomes the task of using that model to build specific use cases, which is why solid libraries and foundational code are so valuable. The goal is to hide the intricacies of the programming language and environment, offering an interface to the vocabulary that is closer to natural language. A common way to do this is by building a DSL.

LLMs are particularly strong here, as they act as a natural-language interface on top of that abstraction vocabulary. A key benefit is that the vocabulary itself is backed by executable code—tests, types, invariants, and other behaviors—which serve as a guardrail for the LLM, helping it self-correct and produce more useful output. For example, using an LLM to generate PlantUML is very effective precisely because a well-defined output vocabulary is already present. This means strong foundational code becomes even more essential in the age of LLMs; the coding process shifts away from raw syntax generation and toward leveraging a well-formed conceptual language to build reliable software.

Code as Its Own Harness

Discussions around context and harness engineering often treat code as a black box. The goal is to manage this box externally by giving the right context in a prompt or by constructing a harness with specs, tests, and static validations to ensure the output is structured correctly. Yet, well-structured code—code built on clear, stable abstractions—inherently acts as the most important part of that harness and context. When the code is well designed, it provides the LLM with both the context and the guardrails needed to succeed.

Consequently, with a solid abstraction layer in place, you gain more freedom in your choice of LLM and rely less on perfectly crafted prompt text. The code structure and its accompanying tests do the heavy lifting, offering the constraints that make LLM output reliable and useful without requiring meticulous prompt engineering.

Evolving Practice

The role of coding is not vanishing, but it is changing. As LLMs make code generation cheap, the act of mechanically writing instructions has less value. However, making the conceptual model explicit and discovering the right vocabulary becomes more central. Refining that vocabulary through iteration and domain expertise is the real work.

This is precisely why programming languages remain vital. You are not meant to be a passive reviewer of generated code; the act of writing code is a fundamental part of the thinking process. Code is instructions for a machine, but it is also a model of understanding, and that second role gains importance in the LLM era. The future of coding is about building better conceptual models, vocabularies, and foundations upon which both developers and their AI tools can operate.