Bolero is a domain-specific language for building CRUD
applications. It is a superset of Go and remains fully
compatible with existing Go codebases.
Bolero is in early alpha. The features below outline where the
language is headed, along with the current state of each.
Features
Enums
Bolero has first-class enum types with exhaustive
switch statements checked at compile time.
A value of an enum type is always one of its declared
variants — illegal states are unrepresentable, and
the compiler, not a code review, guarantees that
every case is handled.
Status: implemented
type Color enum {
Red
Green
Blue
}
func Rose() Color {
return Color::Red
}
func (c Color) Hex() string {
switch c {
Red:
return "F00"
Green:
return "0F0"
Blue:
return "00F"
}
}
func Max(c, d Color) Color {
if c >= d {
return c
}
return d
}
func PrintPalette() {
for c := range Color {
fmt.Println(c.Hex())
}
}
Static properties
Every struct field can carry constant values attached
to the type itself, shared across all instances.
Static properties subsume field tags — typed, checked
by the compiler, and accessible from ordinary code.
Packages can declare their own property groups, so
third-party properties live in a namespace of their
own and never collide.
Status: in progress
type Person struct {
Name string {
MaxLength: 50
}
Age uint
}
type Person struct {
Name string {
MaxLength: 50,
json: {
Name: "name",
OmitEmpty: true
}
}
Age uint {
json: {
Name: "age"
}
}
}
func validate(p Person) error {
if len(p.Name) > p::Name.MaxLength {
return errors.New("too long")
}
}
func jsonKey() string {
return Person::Name.json.Name
}
Components
Components are a general notation for declarative,
tree-shaped structures — not just HTML. The same
syntax describes PDF layouts, web services, even
parsers.
Status: planned, syntax not fixed
comp VendorPage(vendor Vendor){
Heading(){
vendor.Name
},
FieldBox(){
Field(Editable = false){
vendor.Address
},
Field(){
vendor.City
},
Field(MaxLength = 10){
vendor.Vat
}
}
}
comp VendorList(vendors []Vendor){
Heading(){
"Vendors"
},
Table(
Paginate = true,
Data = vendors
){
Column(){
Vendor::Name
},
Column(Sortable = true){
Vendor::City
}
}
}
comp VendorRows(vendors []Vendor){
for _, v := range vendors {
Row(){
v.Name,
v.City
}
}
}
comp Decimal(){
Sequence(){
Optional(){
Choice(){
Literal("+"),
Literal("-")
}
},
Digits(),
Optional(){
Sequence(){
Literal("."),
Digits()
}
}
}
}
Data layer
Models describe persistent data directly in the
language: references, computed aggregates, and
constraints live next to the fields they concern.
Queries are written in Bolero itself and typechecked
against the models. No SQL strings, no runtime
surprises.
Transactions are a block construct: atomic
commits on success and rolls back on error. First-class
support for views, schemas, and indexes is also
planned.
Status: planned, syntax not fixed
type City model {
Id autoincrement
Name string {
Length: 20,
}
PostCode string
}
type Invoice model {
Id autoincrement
Customer references Customer
Amount decimal {
Precision: 12,
Scale: 2
}
}
type Customer model {
Id autoincrement
Name string
Location references City.Name
Spending sum Invoice.Amount
where Invoice.Customer = Name
}
func FindTopCustomer(db Connection) string {
q := with db
from Customer
select Name
orderBy Spending
limit 1
topCustomer, _ := q.Execute()
return topCustomer
}
func DeleteCustomer(db Connection, id int) error {
return atomic with db {
delete Invoice
where Customer = id
delete Customer
where Id = id
}
}
Full-stack framework
Bolero will ship with a full-stack framework built on the
features above, aimed squarely at CRUD-heavy business
applications: ERP, WMS, back offices, and the like.
The framework will cover user management and authentication,
user permissions, localization, reports, mailing, background tasks,
monitoring, retention policies and archiving, and many more things...
Development
Join development of Bolero on GitHub and influence the language design.
The plan is to have beta before 2027, and v1.0 release before 2028. After that, as Go, we will promise full backward compatibility.
Why?
But, why?
Somewhere in the last decade we forgot how efficient we once
were at building business applications. We traded the ugly but
solid interfaces of legacy enterprise software for mountains
of JavaScript, and along the way abandoned basic principles of
human interface design in the chase for the next shiny thing.
Tools like NAV, FoxPro, Microsoft Access, Sybase, Omnis, and
countless other RAD platforms failed to evolve with their
developers. But they clearly got something right: applications
written in them decades ago (often by people who were not
software engineers) are still running in production today.
Why not use existing low code solution?
Mostly because majority of existing tools is commercial.
Idea of Bolero is to give opportunity even to smallest organizations/teams
to build custom applications without need to invest any money.
Why Go?
Go offers excellent performance, trivially simple deployment,
and a mature, still-growing ecosystem. Its compatibility
promise makes it a stable foundation to build a language on.
The many
languages that already target Go show how it is an attractive
platform for new languages.
Why no algebraic data types?
Bolero try to stays as close as possible to the database,
minimizing the object–relational impedance mismatch.
Sum types widen that gap: relational databases have no
natural representation for them. The exception is enums,
sums of singletons, which map cleanly and are fully
supported.
Why no feature X?
I have some more features in my head, but still without a solid design.
For example, maybe it would be bee good to implement default values for struct fields and function parameters.
Also a less explicit error handling would maybe be a good idea.
I am open to any idea that serves the goal of the language - make building CRUD applications simple.
If you have an idea, don't hesitate to open an issue on the repo.
Why use Bolero in the age of AI?
DSLs like Bolero sit between general-purpose languages and
natural language, and inherit trade-offs from both sides. They
are more formal than natural language and, within their
domain, more expressive than a general-purpose language. In
exchange, they demand somewhat more technical skill than
prompting an LLM, and they are less flexible than a
general-purpose language.
The first trade-off is softened by pairing Bolero with LLMs
an approach whose benefits are described well in the article
LLMs
and DSLs.
The second is addressed by leaning on Go itself: for every
feature Bolero adds, a plain Go construct remains available as
a substitute. Ultimately, Bolero will let you transpile any
Bolero source to pure Go.
Finally, while Bolero works well with AI, it also aims to
remove some of the need for it: developers without access to
LLMs should be just as productive in building CRUD apps.
And I do believe, that developer with clear idea and well designed
DSL can outperform LLM.