Ballerina: A Programming Language for Cloud Native Computing
The Ballerina programming language was born from a frustration of how to program an enterprise service bus (ESB), software designed to connect different enterprise applications.
“We were working on improving our language for ESB configuration for a long time, looking for the right way to evolve it,” said the language’s creator, Sanjiva Weerawarana, CEO and founder of WSO2. Eventually, it became obvious, however, that the traditional approach of using data flow as a metaphor for integration was not a good fit. “It became clearer and clearer to me that that was not the right model,” Weerawarana said. “The configuration approach wasn’t powerful enough to do what we wanted to do with how integration should be programmed.”
Nor did existing programming languages do the trick. They were not designed to easily handle integration tasks, such as bringing in an external source. The work is left to drivers and client libraries, necessitating additional work on the part of the developer (as well as potential bugs and performance issues).
The issue at hand was a fundamental shift in what programmers are doing today, as more move to the cloud native model. Very few applications in this realm are stand-alone entities. Many organizations are moving towards a microservices approach, in which individual components communicate with one another to provide a service. And all this disaggregation leads to more endpoints, accessible via APIs. “Increasingly the applications that we are building are no longer living within a single machine,” Freemantle said.
To create Ballerina, Weerawarana drew on a tool WSO2 often used to work through client deployments, the sequence diagram. Weerawarana led the efforts to build out the language (He’s had experience writing languages, and even taught a university course on the subject). Eventually, the company built up a team of 60 developers to bring the language to fruition (The name “Ballerina” came from Weerawarana’s fascination with how ballets are very tightly coordinated, resulting in a graceful flow that any good enterprise integration should aspire to).
Ballerina treats network services as a first-class concept, easily integrated into all other aspects of the program. “So the fact that this is a full programming language that understands these network concepts means that you stay in the zone as a developer,” Fremantle said. Ballerina is not a domain specific language (DSL) only for integration tasks. It actually is a full-fledged programming language, one that Weerawarana can envision replacing Java over the next decade.
In the long run, middleware tools such as ESBs and application servers will no longer be needed, Weerawarana predicted. The routing capabilities of an ESB, for example, will either be embedded in the code, using a language such as Ballerina or assumed by orchestration engines such as Kubernetes or service mesh software such as Istio.
Recently, WSO2 held its first user conference, Ballerinacon in San Francisco, where attendees learned all about how Ballerina may be the world’s first cloud native programming language.
Here are six features of Ballerina that make it a programming language for cloud native computing.
1. Built-in Container Support
Ballerina can do a lot of the basic preparations for running programs in cloud native formats, a time saver for developers who already know what execution environment their programs will have. Code can be annotated so that the compiler will automatically create a Dockerfile, and package it within an image that then can be run as a container. Because the compiler can do all this extra work, the developer can build and test multiple iterations much more quickly.
Ballerina also offers a library for the Kubernetes open source container orchestration engine, which can be handy if you want to coordinate the operations of multiple programs through a single command.
Learn more here.
Ballerina is natively supported by the OpenWhisk platform and WSO2 itself runs a managed OpenWhisk service, called the WSO2 Serverless Solution. From the OpenWhisk command line, you upload your function and the service compiles it and places it within a container. The service provisions the resources actually needed, autoscaling with the load.
Kubeless provides a way to manage Ballerina programs as serverless functions, using Kubernetes. Kubeless sets up a custom resource definition for the function, so it can be managed by Kubernetes, and accessible through the Kubeless command line.
One of the interesting aspects of Ballerina is that any function can be turned into an API endpoint, in effect making each function its own stand-alone microservice. In turn, this opens up the use of a single semantic domain for integration operations. This allows you to generate a Swagger file that can be used for an open API gateway.
Learn more here.
Ballerina programs are observable by default: Ballerina supports microservices-based logging, tracing, and metrics. Instead of adding in an extra library, the binary generated by Ballerina pumps such data out by default. Users don’t have to do anything extra for observability features, aside from configuring the end-clients to interpret and display the data.
The commercial Honeycomb software, used for system debugging, and by the open source Prometheus time-series monitoring, are two platforms that work easily with Ballerina programs. For tracing, the data generated already fits the OpenTracing model. For visualization, it dovetails nicely with Grafana. The log data can be easily digested by the ELK stack or written out to a flat file. There are various connectors for SQL-based database systems, and there is a user API for developers to create custom metrics.
Learn more here.
4. Works With, or Without, a Service Mesh
Due to the nature of many microservices communicating with one another, the role of the service mesh has become increasingly vital in such operations, where it takes care of duties such as service discovery (microservices finding one another in a dynamic system) and authentication. Ballerina is well-equipped for such an environment.
Ballerina works well if you decide to use a service mesh such as Istio. You just need to manually inject a sidecar by creating a single YAML file. This is useful for adding Istio-specific services such as virtual service gateways. The idea here is that the network functions can be handled by Istio (for the control plane) and Envoy (for the data plane) while keeping Ballerina for the business logic itself.
However, Ballerina, written as an integration language, after all, can also carry out service mesh-like duties, providing built-in functionality such as service discovery, observability, and multi-protocol support. It can also support those cases where a control or data plane logic must be encapsulated within the code itself. Learn more here.
5. Easy Stream Processing with Streaming SQL
Increasingly today’s sources of data aren’t just static in nature. Often such material is collected as soon as it is generated, from a stream processing system such as Apache Kafka.
Ballerina has capabilities for working with streaming data by performing complex event processing (CEP). It does this through a variant of streaming SQL, a variation of SQL known by most developers with extensions for processing a collection of events over time windows. You can set up a table with the required fields, and then do a forever block, which means it keeps reassessing the data as it is refreshed.
In Ballerina, network events are native, first-class concepts. Developers can establish service endpoints and service clients that come with rich annotations and filters. Also, popular data exchange formats such as JSON and XML are their own data types, allowing developers to program against the data directly.
6. Baked-in Security
Security is baked into Ballerina as well, with a set of security checks implemented at the IDE and with a security-aware compiler. The compiler does a static analysis, inspecting the code for obvious flaws such as possible buffer overflows.
There is also runtime analysis. Every function has built-in security annotations. When evoked, the @sensitive annotation will only accept trusted data. This could prevent SQL injection attacks, for instance. The @tainted annotation will mark any computation that are deemed untrusted.
Learn more here.