Why declarative DSLs are the right substrate for AI-built software
Level 1 of 3: the general argument. Level 2 is the JFaster language; level 3 is its implementation.
AI agents write code well. What they do badly is keep a large system consistent. The failures are predictable, and they don't come from a lack of intelligence. They come from the shape of conventional codebases.
The problem: behavior is spread across layers
In a conventional business application, one concept, say "an invoice has a due date", lives in many places:
- a database column and its migration
- a field in an entity class, plus getters and setters
- a mapping (ORM annotations or XML)
- a data transfer object and its serializer
- a validation rule
- a form input, a listing column, a filter
- an API schema and its documentation
- tests for all of the above
Adding or changing that concept means touching all of them in a consistent way. An agent working on such a codebase has two bad options:
- Read everything. The codebase is thousands of files. It doesn't fit in the context window, and reading it is slow and expensive.
- Read a sample and infer the rest. This is where agents invent a field that doesn't exist, call a service with the wrong signature, or update three layers and forget the fourth.
The solution: describe the application, derive the code
A declarative domain-specific language turns the problem around. Instead of writing the layers, you describe the application once: its data, relationships, queries, screens and operations. A generator derives every layer from that description.
Invoice @Billing order="dueDate"
customer Customer d r
dueDate date ds r
lines InvoiceLine set ei x childproperty="invoice"
Those four lines are the whole definition of the invoice: storage, form, listing, relationships and API. For an agent this changes everything:
| Conventional codebase | Declarative model | |
|---|---|---|
| What the agent must read to understand the system | thousands of files; in practice, a sample | the whole model, which fits in context |
| Where a field is defined | in 5 to 10 places that must agree | once |
| Size of a typical change | many files across layers | a few lines in one place |
| What can go out of sync | anything the agent did not read | nothing: the layers are derived |
| How to review the change | a large diff across layers | a small diff that reads like a spec |
| What the agent must know | the framework, the project's conventions, its history | a small, documented vocabulary |
What makes a DSL good for agents
Not every DSL helps. The properties that matter are:
- Compact. A realistic application must fit in a context window with room to spare. One line per field is the right order of magnitude.
- Complete for its domain. If common needs (queries, filters, master/detail, operations, conditional fields) can't be expressed, the logic leaks back into code and the single source of truth is lost.
- Small vocabulary, fixed semantics. An agent can learn it from one specification and write it without guessing.
- An escape hatch into ordinary code for the part that is genuinely custom, without duplicating what the model declares.
- Proven. The language has to be tested on real systems, not only on demos, so that its constructs cover what business applications really need.
The last point is the hardest to achieve. It takes years of production use to learn which constructs a business domain needs.
Where JFaster fits
JFaster is a declarative language for data-centric business applications, with about 20 years of production use: around 75 projects and systems with up to 500 entities. It meets all five properties above.
Next: the JFaster language specification · patterns · examples · guide for agents