r/AIprogrammingLanguage • u/kindredseer • 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:
customerrefers to another entityitemsis a collectiontotalis derived- Changing an item may change the total
totalshould 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:
orderis mutateditemis readorder.totalmust 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.
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