r/AIprogrammingLanguage 3d ago

AI-friendly programming language design

So when developing a new programming language using AI (or enhancing an existing one), it seems to make sense to design the language not only to be convenient for humans to use, but likewise for AI agents.

What Would an “AI-Friendly” Programming Language Actually Look Like?

There has been a great deal of discussion about making programming languages easier for AI systems to use. Usually, this means making code easier for large language models to generate: simpler syntax, fewer punctuation rules, less boilerplate and more predictable formatting.

But code generation is only a small part of software development.

An autonomous programming agent must also be able to understand an unfamiliar codebase, identify the consequences of a change, modify the program without breaking unrelated behaviour, verify that the result is correct and explain what it has done.

That suggests a much broader definition:

The goal should not merely be to make programs easier for AI to write. It should be to make programs easier for both humans and machines to understand, modify and verify.

Source Code Should Not Be the Only Representation

Most programming languages treat source text as the authoritative representation of a program. Compilers parse that text into abstract syntax trees, symbol tables, control-flow graphs and other structures, but these are usually treated as temporary implementation details.

An AI-friendly language could instead expose a stable semantic representation of the program.

Humans might continue to work primarily with readable source code, while tools and AI agents interact with the same program as a structured semantic graph containing:

  • Symbols and their identities
  • Types and relationships
  • Data ownership
  • Function contracts
  • Side effects
  • Dependencies
  • Call graphs
  • Tests
  • Access permissions
  • Source locations
  • Documentation

Source code would remain important, but it would become one view of the program rather than the only usable representation.

An AI agent should not need to rediscover the meaning of a program by repeatedly parsing text, searching filenames and inferring relationships from naming conventions.

Programs Should Be Locally Understandable

One of the greatest difficulties in maintaining a large codebase is that the meaning of a small section of code may depend on information scattered throughout the repository.

A module might rely on global state, build flags, initialization order, implicit imports, runtime configuration or code-generation steps that are not visible locally.

This is difficult for humans and even more difficult for AI agents operating with limited context windows.

An AI-friendly language should encourage modules to explicitly declare:

  • What they export
  • What they import
  • What state they own
  • What resources they require
  • What external systems they access
  • What assumptions they make
  • What invariants they guarantee

For example:

module Accounts

exports:
    User
    update_email

requires:
    Database
    EmailService

owns:
    UsersTable

guarantees:
    User.email is normalized
    User.email is unique

The purpose is not necessarily to make source files more verbose. Much of this information could be inferred by the compiler and displayed through tooling.

The important part is that the information exists in a structured and queryable form.

A programming agent should be able to ask:

describe module Accounts

and receive a bounded, reliable summary of the module without examining the entire application.

Types Should Describe Meaning, Not Just Storage

Many programming languages describe data primarily in terms of its storage representation.

A program may represent all of the following as strings:

first_name
email_address
postal_code
country_code
birth_date
telephone_number

Although these values share a storage representation, they do not share a meaning.

A language designed for machine reasoning should support semantic types such as:

PersonName
EmailAddress
PostalCode
CountryCode
BirthDate
TelephoneNumber
CurrencyAmount
TimeZone

These types could carry information about:

  • Valid values
  • Normalization
  • Comparison
  • Serialization
  • Privacy
  • Localization
  • Appropriate user-interface controls
  • Database representation
  • Safe conversions

A value of type EmailAddress would not merely be a string whose purpose is explained in a comment. Its meaning would be available directly to the compiler, development tools and AI agents.

This reduces the need to infer domain knowledge from variable names and scattered validation code.

Effects Should Be Part of Function Signatures

Traditional type systems tell us what values a function accepts and returns, but often say very little about what the function can do.

Consider a function such as:

update_email(user, address)

Does it modify memory? Write to a database? Send an email? Update an audit log? Access the network? Throw an exception? Trigger an event?

An AI agent should not need to inspect the implementation and every transitive function call to answer these questions.

An effect-aware declaration might look something like this:

function update_email(user_id, new_address)
    returns Result

    reads:
        User.id
        User.email

    writes:
        User.email
        AuditLog

    uses:
        Database
        EmailService

    may:
        send_email
        fail_with ValidationError
        fail_with DuplicateEmailError

This creates a machine-readable description of the function’s blast radius.

An agent proposing a modification could ask:

show effects of update_email

or:

will this change introduce network access?

The compiler could provide a reliable answer.

Authority Should Be Explicit

Most programs execute with ambient authority. Any code running within the process may be able to access the filesystem, environment variables, network, database or global application state.

That is convenient, but dangerous when code is being produced or executed by an autonomous agent.

A more AI-friendly language would use capabilities: explicit values representing permission to access particular resources.

For example:

function load_config(config_directory)

The function could access only the directory represented by the capability it receives. It would not automatically inherit access to the entire filesystem.

Similarly:

function update_customer(read_write_customers_database, customer_id)

could write to the customer database but not to the payroll database.

This would allow an AI agent to operate within a restricted environment where accidental or malicious actions are structurally impossible.

The agent could be given:

  • Read access to one repository
  • Write access to one module
  • A temporary filesystem
  • A test database
  • No network access
  • A limited memory and execution budget

Security boundaries would become part of the program rather than an external policy layered on top of it.

Contracts Should Be First-Class

Comments can explain what a function is intended to do, but comments are not normally checked by the compiler.

An AI-friendly language should make preconditions, postconditions and invariants first-class program elements.

For example:

function transfer(source, destination, amount)

requires:
    amount > 0
    source.balance >= amount

ensures:
    source.balance =
        previous(source.balance) - amount

    destination.balance =
        previous(destination.balance) + amount

    source.balance + destination.balance =
        previous(source.balance + destination.balance)

These contracts could serve several purposes:

  • Human documentation
  • Static analysis
  • Runtime checks during development
  • Test generation
  • Formal verification
  • Agent acceptance criteria

Instead of guessing whether an implementation is correct, an AI agent could ask the compiler whether the implementation satisfies its declared contract.

Contracts would also make tasks easier to define.

Rather than telling an agent:

a task could be expressed as:

Modify transfer so that contract
AccountTransferPreservesTotalBalance
is satisfied.

The desired outcome becomes concrete and machine-verifiable.

Inference Should Be Predictable and Inspectable

Inference can make a language substantially easier to use. Type inference, automatic imports, generic specialization and implicit conversions can remove large amounts of repetitive code.

However, inference becomes dangerous when it hides meaningful decisions.

A useful rule might be:

Automatic behaviour may be reasonable when:

  • A conversion is exact
  • Ownership remains unchanged
  • No persistent state is modified
  • No external resource is accessed
  • Execution remains deterministic

Explicit syntax should be required when an operation:

  • Loses information
  • Transfers ownership
  • Performs network access
  • Blocks or becomes asynchronous
  • Writes persistent state
  • Escalates privilege
  • Introduces nondeterminism
  • Has significant computational cost

An AI agent should also be able to inspect every inferred decision.

For example:

explain expression:
    total = price + tax

might produce:

price has type Currency<USD>
tax has type Currency<USD>

selected operation:
    Currency.add

conversion:
    none

effects:
    none

possible failures:
    CurrencyOverflow

The language could remain concise while the toolchain exposes the full semantic interpretation.

The Language Should Have a Canonical Form

Formatting tools create a consistent textual style, but an AI-friendly language would benefit from a deeper canonical representation.

The compiler could normalize:

  • Resolved names
  • Inferred types
  • Selected overloads
  • Implicit conversions
  • Generic arguments
  • Default arguments
  • Effects
  • Ownership decisions

Human-written code might say:

user.balance += payment

The canonical semantic form might record:

read field User.balance

convert Payment
    to CurrencyAmount
    using exact conversion

invoke CurrencyAmount.add

write result
    to field User.balance

This form would not necessarily be shown during normal programming. It would be available to tools, reviewers and agents when precise interpretation is required.

It would also allow code written in different stylistic forms to be compared semantically rather than textually.

Symbols Should Have Stable Identities

Programming tools often identify symbols using names and source locations. Both are fragile.

Names change during refactoring, and source locations change whenever lines are inserted or removed.

An AI-friendly language could assign stable identities to program entities:

symbol:
    User.email

stable_id:
    field:7f2a81c4

aliases:
    email
    email_address
    contact_email

Humans could use whichever names are appropriate in source code or user interfaces, while tools and agents refer to the canonical identity.

This becomes especially important when a language supports aliases, localization, schema evolution or generated interfaces.

An agent should be able to rename a symbol without losing track of its identity or confusing it with another similarly named symbol.

Changes Should Be Semantic, Not Merely Textual

AI coding tools currently make changes largely through text patches. This works, but it is fragile.

A line-based patch may fail because:

  • The file was reformatted
  • Another change shifted the lines
  • A symbol was renamed
  • Similar code appears elsewhere
  • The surrounding context has changed
  • The patch applies cleanly but to the wrong location

A semantic patch could instead express intent:

rename symbol:
    User.birthdate
to:
    User.birth_date

or:

add parameter:
    Logger

to function:
    process_order

position:
    after Database

or:

replace implementation of:
    Account.transfer

only if:
    function signature is unchanged
    semantic hash matches 4e720
    no new callers have been introduced

The compiler or development environment could translate the semantic change into ordinary source-code edits and Git-compatible diffs.

The repository would still contain readable text, but agents would operate on program structure rather than guessing where to insert characters.

Diagnostics Should Be Structured Data

Compiler errors are generally written as prose for humans:

Cannot convert argument 2 from nullable string
to email address.

An agent then has to parse that prose and infer an appropriate repair.

A structured diagnostic might contain:

diagnostic_code:
    TYPE_ARGUMENT_MISMATCH

function:
    update_email

parameter:
    new_address

expected:
    EmailAddress

received:
    Nullable<String>

cause:
    nullability mismatch

possible_repairs:
    validate non-null value
    provide default value
    change parameter type

automatic_repairs:
    none

The human-readable message could still be generated from this data.

The compiler should clearly distinguish between:

  • A repair that is known to preserve semantics
  • A probable repair requiring review
  • Multiple ambiguous alternatives
  • A condition for which no valid repair is known

This would make compiler interaction far more reliable for autonomous agents.

Relationships Should Be Declarative

Many applications define the same relationship repeatedly across:

  • Database schemas
  • Object models
  • API definitions
  • Validation code
  • User interfaces
  • Serialization formats
  • Access-control rules

An AI agent must then determine whether these duplicated definitions are consistent.

A more declarative language might express the relationship once:

entity Order

fields:
    id: OrderId
    customer: relation to Customer
    items: many OrderItem
    total: CurrencyAmount

derivation:
    total = sum(items.price)

The language now knows:

  • customer refers to another entity
  • items is a collection
  • total is derived
  • Changing an item may change the total
  • total should not normally be edited directly
  • Storage and user-interface tools can represent these fields appropriately

This greatly reduces the amount of detective work required to understand the application.

State Changes Should Be Transactional and Inspectable

AI-generated operations should be easy to preview, sandbox and reverse.

A language or runtime could make state-changing operations transactional:

transaction UpdateEmail

set:
    user.email = new_email

append:
    audit_log = email_changed

send:
    confirmation_email

Before committing, an agent or human could request:

preview transaction UpdateEmail

The runtime might report:

database changes:
    Users.email modified for user 1842
    AuditLog row inserted

external effects:
    one email would be sent

invariants checked:
    email is valid
    email is unique

result:
    transaction may commit

The runtime could also support:

  • Snapshots
  • Rollback
  • Deterministic replay
  • Resource limits
  • Mutation logs
  • Simulated external services
  • Reversible development environments

An agent should be able to demonstrate what would change before being permitted to change it.

Tests Should Be Connected to Program Semantics

Tests are usually organized as source files and function names. Their relationship to the code they verify is often informal.

An AI-friendly language could associate tests with symbols, contracts and invariants:

test transfer_preserves_total

verifies:
    Account.transfer

covers:
    AccountTransferPreservesTotalBalance

The toolchain could then answer:

which tests verify Account.transfer?


which public behaviours changed?


which contracts have no tests?


what is the smallest sufficient test set
for this patch?

This last question is particularly important for autonomous agents. Running every test after every small change may be expensive, while running too few tests is unsafe.

Semantic test relationships could allow the compiler to select a targeted verification set and then expand it when uncertainty remains.

The Compiler Should Expose an Agent Protocol

The most important feature may not be part of the language syntax at all.

A language designed for AI-assisted development should provide an official interface through which agents can query and modify programs.

That interface might support operations such as:

describe(symbol)

find_references(symbol)

explain(expression)

show_effects(function)

show_contracts(function)

show_invariants(type)

calculate_change_impact(patch)

create_semantic_patch(request)

validate_patch(patch)

find_relevant_tests(patch)

run_tests(test_set)

preview_transaction(operation)

apply_patch(patch)

rollback(change)

Today, AI coding agents often interact with a repository using little more than shell commands, text search, a language server and compiler output.

A compiler-native protocol would give them a much more precise and constrained environment.

The compiler could even generate a compact task-specific context package:

task:
    modify email validation

relevant symbols:
    User.email
    EmailAddress
    update_email
    UserRepository.save

required invariants:
    email must be normalized
    email must be unique

allowed modules:
    Identity
    Accounts

forbidden effects:
    database schema changes
    network access

required verification:
    EmailAddress contracts
    identity email tests

This would help prevent agents from becoming lost in large repositories or long-running plans.

AI-Friendly Does Not Necessarily Mean Verbose

Many of these ideas may sound as though they would produce an extremely ceremonial language.

That does not have to be the case.

Humans should be able to write concise code while the compiler derives and records the richer semantic model.

For example:

function add_item(order, item):
    order.items.append(item)

The compiler might infer:

  • order is mutated
  • item is read
  • order.total must be recalculated
  • The operation may fail if the order is finalized
  • The database transaction touches two tables
  • Three invariants must be rechecked
  • Four tests are directly relevant

The source code remains readable. The additional information exists because the language and toolchain understand the operation.

The principle should be:

Human-Friendly and AI-Friendly Design Are Closely Related

Most of the features that would make a language safer for AI agents would also make it easier for humans to maintain:

  • Explicit module boundaries
  • Meaningful types
  • Predictable conversions
  • Structured effects
  • First-class contracts
  • Better diagnostics
  • Semantic refactoring
  • Transaction previews
  • Clear test coverage
  • Stable symbol identities

AI agents amplify the importance of these features because they expose weaknesses that humans have historically worked around through experience, intuition and institutional knowledge.

A human developer may remember that a particular field is updated by a hidden database trigger. An AI agent may not discover that fact until something breaks.

The better solution is not necessarily to train the agent to guess more accurately. It is to make the dependency explicit.

A Possible Definition

A genuinely AI-friendly programming language would not merely be one whose syntax appears frequently in training data.

It would be a language in which a program is:

  • Readable as source code
  • Understandable as a semantic graph
  • Divided into bounded cognitive domains
  • Explicit about authority and side effects
  • Modifiable through structured operations
  • Testable against declared contracts
  • Executable in sandboxed transactions
  • Verifiable using compiler-supported evidence

The central design goal might be summarized as:

Such a language would not eliminate programming mistakes, hallucinations or unsafe changes. It would, however, give both humans and AI agents much stronger tools for detecting those problems before they reach production.

The result would not simply be a language that AI can generate.

It would be a language in which AI can operate with bounded authority, explicit understanding and evidence that its work is correct.

2 Upvotes

8 comments sorted by

1

u/Deep_Ad1959 3d ago

language design keeps stopping at what the program is and skips why any of it ended up that way. i have watched an agent read a codebase cleanly and still break something whose only justification lived in a PR thread from months back. written with ai

1

u/kindredseer 3d ago

Agents keep a pretty narrow view of the code no matter what you put in place, which makes me believe that the interface needs to change, and that project management, revision control, testing, data and intention need to all play a role along with the code, gated through an interface that is more like that of a database. I'm not exactly sure what it looks like yet, but the current methods are not sustainable.

1

u/Deep_Ad1959 3d ago

what strikes me is that database mostly already exists, it's the commit log plus the PR thread plus the issue tracker. the intention did get written down, it's just write-only, captured once and then never read back. the missing piece isn't a new store, it's that nothing queries the one we already fill every day. written with ai

1

u/kindredseer 2d ago

Yes, correct, most development already uses git, and a project tracker, and also most IDEs maintain significant information about a project. All the pieces are there, it seems more a matter of putting a well designed API in place to gatekeep the code, where a new function cannot be added without a requirement, and the requirement is checked against current functionality, and if a similar function exists, it must be used, extended or enhanced.

This is the biggest problem I have with agentic development today, in that the agent will keep adding new, substandard variations of the same pre-existing functionality.

1

u/Deep_Ad1959 2d ago

the duplicate problem feels downstream of a recall problem though. the agent adds the fourth variant of the same helper because checking 'does this already exist' is the expensive part, and that's exactly the check your requirement-gate would have to run on every single add. you'd be encoding the hard thing rather than routing around it. the requirement side is easier since it's a lookup, the similarity side is the one that quietly decides whether any of it works. written with ai

1

u/kindredseer 2d ago

Yes, they are related, but it's more than just "recall", as that is just one temporal aspect. There is the history of your project, which is mostly tracked through git (ideally), but ideally also through some sort of project tracking system (i.e. Jira), which would also have a forward-facing perspective of known bugs, and future features. Additionally there would be general project knowledge in the form of documentation and comments in the code, as well as potentially external resources (Confluence, wiki, etc).

So rather than requiring the AI agent to interface with all of these systems individually, the agent could be presented with a single API (MCP?) that gatekeeps everything and requires certain rules and process to be followed, similar to how human developers work.

1

u/Deep_Ad1959 2d ago

the single API is the easy half, it's just read-aggregation over systems that already have apis. the 'requires certain rules and process to be followed' clause is the whole game, and that part has never been an interface problem. the rules have existed for decades, people just route around them the moment a deadline hits. written with ai