First impressions of the new Cloud Native programming language Ballerina

Nowadays everything is Cloud Native; everybody talks about CN tools, frameworks, solutions, and so on. On the other hand those tools totally changed the way we design, develop, test and release modern applications. I think the number of issues which we solved with the new concepts is equal with the number of new challenges, so in short we simply shoveled problems from one hole to the other. Many new tools appeared on the market to make developers' life easier by
  • integrating software with the underlying infrastructure
  • watching file changes and building containers automatically
  • generating resource descriptors on the fly
  • allowing debugging in a running container
  • etc.
Next to the new tools, new programming languages such as Metaparticle, Pulumi or Ballerina have been born. The last one had my attention because others are extensions on top of some existing languages, while Ballerina is a brand new programming language, designed from scratch.
My first impression of it was this language is a creature of Dr Frankenstein because it is a mix of 4+ existing languages with a few unique and interesting features. But first let's talk about what CN programming language means?

First of all there is no clear definition however here is my personal opinion. Beginning with the most important quality, it has handy support to create and publish (micro)services easily. It must contain REST and RPC connectors with data representation/conversion to communicate with other services language-natively. Circuit breaker, client side load balancer, fail-over and retry logic are also very convenient features. And finally it has to support containers and orchestration platforms like Kubernetes. The same application has to run locally or in the public cloud.

The language itself is a compiled, transactional, statically and strongly typed programming language with textual and graphical syntaxes. The compiler is written in Java. It generates an intermediate byte-code, which looks like Java but it isn't compatible with JVM. The byte-code runs on Ballerina Virtual Machine. The first version was in Java with GC, but later on they re-implemented the VM and switched to a register based technology (for more information please visit this page). The language has textual representation a.k.a. source code, and an editable flow diagram view (image from dzone.com).

If you are familiar with Java, Groovy, Go and Python you will be familiar with Ballerina. Similarities are:
  • Java
    • Object
    • Generic
    • Annotation
    • JUnit
    • JDBC
    • JMS
  • Groovy
    • Fork join and async library
    • Elvis operator
    • Nil lifting
    • String template literal
  • Go
    • Struct type, called record
    • Functions are first class citizens
    • Function declaration
    • Dynamic interface implementation
    • Panic and error handling
    • Multi return value
    • Ignore variable "_"
    • Channels
  • Python
    • self.
    • __init()
I created a small application to demonstrate the language itself. My goal was to fill the project with as many features as possible, so please forgive complexity of examples. The application in nut shell: it exchanges gold to different currencies (ratios are hard coded, so use at your own risk :D). So we have 3 grams of gold and it calculates the best price for example in USD. It has a CLI interface, and uses 3 services to calculate the final price. Two of them serve the exchange ratio and the third one calculates the price.

First I would like to introduce the simplest exchange service, which contains only the basic language features.

  • Line 3: Our service will listen on port 9091.
  • Line 5: It has an endpoint called "getRate".
  • Line 6: It generates the response message with a type json. Built in types are: int, float, decimal, byte, string, boolean, anydata, any, nil, json, xml, array, map, tuples.
    • Line 7-8: New http:Response initialization with JSON payload.
    • Line 9:- Sends the response to the caller, "_" means the application ignores the return value.
    This was obviously easy. Let's implement the response generator.

    • Line 1: @untained is a special annotation and is required because the compiler checks major security vulnerabilities including SQL injection, path manipulation, file manipulation, unauthorized file access, and unvalidated redirects (open redirects). @untained annotation marks return content as manually secured.
    • Line 1: Input parameter "string|error payload" looks weird, it's called union type, and means the type of payload is either string or error. Union type is the most questionable language behavior. It looks bad practice at first glance, but on the other hand Ballerina has many features witch make them easier to use (match, type guard) or at least make them make sense.
    • Line 2: Type check, a type-guard check is a type test construct that allows selective code execution based on the type of the expression that is being tested. That means in line 3, payload is a string but an error in line 4.
    • Line 3: Json is a built in type, so it's really easy to declare and use it.
    • Line 4: Power of string template literals.
    • Line 8: "match" is a cleverer switch, with support of simple, tuple and record types (I will explain later).
    The first service is ready to use.

    # ballerina run exchange-simle.bal
    Initiating service(s) in 'exchange-simple.bal'
    [ballerina/http] started HTTP/WS endpoint 0.0.0.0:9091
    (press Ctrl+c after curl to exit)
    
    # curl -d "EUR" http://localhost:9091/exchangeSimple/getRate
    {"currency":"EUR", "rate":35.5}
    


    As I mentioned earlier, the CLI tool talks with two services to figure out the best price for exchange. The previous version was the simplest one. In the next section I want to present an enterprise grade version of the same service.

    • Line 1: Define a custom error type.
    • Line 3: Our first Ballerina object. The concept isn't powerful like in Java. There are only two types of them, abstract and regular (non-abstract) objects. An abstract object can only contain member declarations and/or method signatures - method body isn't allowed. An instance automatically implements abstract class if members and methods match it - like Go interfaces.
    • Line 4: Member definition: the visibility is limited to public or private and modifiers (transient, final, static) are not allowed.
    • Line 6: Constructor of the object.
    • Line 12: Built in collections support immutability by freezing.
    • Line 17-24: Iterable collections have high order functions such as map, filter, average, sum, max, min, etc. The only problem with them is, they don't support closures as function in the current version (0.990.0) so it's impossible to use the outside context during execution. The code above compiles but throws runtime exception.
    • Line 18: Split tuple into separated variables. The number of elements in the tuple aren't limited to two, so the well known left() and right() don't exist in Ballerina. The only way to get access to the content of a tuple is to split it into new variables.
    • Line 28: Create a custom error.
    Next part of the application is the second service itself.
    • Line 2: Member declaration. Services are special objects in the background. Constructor and members are allowed, but functions not?? (i hope they will fix it as soon as possible).
    Last part of the service is the response generation.

    • Line 1: Records are Data Transfer Objects.
    • Line 4: Optional field definition. By default all fields are mandatory in a record. Be careful with optional fields, because they throw KeyNotFound error in runtime!
    • Line 10: Member function declaration without body?? GOTO 13
    • Line 13: Go style function attach to on object.
    • Line 14: "check" is a developer friendly error handling mechanism. In this case the return value of getTextPayload() is a union type string|error. The function return value is also a union type json|error. Because our function returns an error, check can replace manual error handling and returns the error of the line execution immediately as function return value.
    • Line 17: Ballerina has built in converters for json, xml, map, record, so it's very easy to transform from one type to the other.
    Enterprise version of the exchange service is done. It's now time to create the calculator service (only multiply implemented)

    • Line 2: Custom type with limited number of values.
    • Line 9: There are two types of record, closed and open. By default all records are open, which means any number of additional fields can append during initialization. "!..." sign closes the record, so only the listed fields are allowed.
    • Line 13: Nullable value definition. Null pointer error is one of the most common errors in runtime. In Ballerina, null assignment is accepted only for nullable values. Nullable types are separated types, so float and float? are different types!
    • Line 33: JSON to record conversion.
    • Line 34: Record to record conversion.
    The final part of the application is the CLI client. It connects to the exchange services parallel, finds the best price and than calls the calculator service to multiply the amount of gold with the given ratio.

    • Line 4: Global variable with a simple HTTP endpoint configuration.
    • Line 5: HTTP endpoint configuration with retry and timeout. The configuration has built in circuit breaker, client side load balancer, failover between multiple services.
    • Line 19: "untaint" calls Ballerina's built in sanitizer.
    • Line 19: Type casting from int to float.
    • Line 28: Error or nil lifting. Ballerina breaks line execution if right side of "!" is error or null and lifts the result.
    • Line 31: Stop function execution and throw a panic.
    • Line 35-43: Use fork-join to call services parallel.
    • Line 37: Catch panic with "trap" and convert it to a regular error.
    At that point the hard job is done. All of the services are ready and the command line interface glues the functionality together. We can run the whole application on our local machine, however we don't want to. We want to run our microservices on Kubernetes. Of course a CN programming language supports Kubernetes, so there is nothing else to do except annotate our services and Ballerina will generate all the Kubernetes resource descriptors, Helm charts and will build Docker containers in the configured registry.


    # minikube start
    # minikube addons enable ingress
    # eval $(minikube docker-env)
    # ballerina build
    Compiling source
        exchange-simle.bal
        exchange-enterprise.bal
        calculator.bal
        convert.bal
    Generating executables
        ./target/exchange-simle.balx
     @kubernetes:Service     - complete 1/1
     @kubernetes:Deployment     - complete 1/1
     @kubernetes:Docker     - complete 3/3
     @kubernetes:Helm     - complete 1/1
    
     Run the following command to deploy the Kubernetes artifacts:
     kubectl apply -f /Users/mhmxs/GitHub/ballerina-gold-exchanger/target/kubernetes/exchange-simle
    
     Run the following command to install the application using Helm:
     helm install --name exchange-simle-deployment /Users/mhmxs/GitHub/ballerina-gold-exchanger/target/kubernetes/exchange-simle/exchange-simle-deployment
    
        ./target/exchange-enterprise.balx
     @kubernetes:Service     - complete 1/1
     @kubernetes:Deployment     - complete 1/1
     @kubernetes:Docker     - complete 3/3
     @kubernetes:Helm     - complete 1/1
    
     Run the following command to deploy the Kubernetes artifacts:
     kubectl apply -f /Users/mhmxs/GitHub/ballerina-gold-exchanger/target/kubernetes/exchange-enterprise
    
     Run the following command to install the application using Helm:
     helm install --name exchange-enterprise-deployment /Users/mhmxs/GitHub/ballerina-gold-exchanger/target/kubernetes/exchange-enterprise/exchange-enterprise-deployment
    
        ./target/calculator.balx
     @kubernetes:Service     - complete 1/1
     @kubernetes:Deployment     - complete 1/1
     @kubernetes:Docker     - complete 3/3
     @kubernetes:Helm     - complete 1/1
    
     Run the following command to deploy the Kubernetes artifacts:
     kubectl apply -f /Users/mhmxs/GitHub/ballerina-gold-exchanger/target/kubernetes/calculator
    
     Run the following command to install the application using Helm:
     helm install --name calculator-deployment /Users/mhmxs/GitHub/ballerina-gold-exchanger/target/kubernetes/calculator/calculator-deployment
    
        ./target/convert.balx
    # make kube-apply
    # make expose
    # ballerina run convert.bal 3 USD
    Handling connection for 9091 (call simple exchange service)
    Handling connection for 9092 (call enterprise exchange service)
    Handling connection for 9092 (the service doesn't work as I mentioned)
    Handling connection for 9092 (CLI retries a few times)
    Handling connection for 9090 (call calculator service)
    106.5
    # make cleanup
    


    I hope this introduction to Ballerina language was interesting for all. The source code of the application is available on GitHub. Ballerina by Example is a beautiful collection to learn the language, so don't forget to visit it. For development I used Visual Studio Code plugin which worked well including syntax highlighting, auto-completing, jumping to declaration, finding all references and debugging (rename was broken). Intellij IDEA plugin is also available in the marketplace. There is one important thing to mention about IDE plugins. They require Java 8, so be sure you set the right version of Java.

    There are some other nice features which make the language more powerful. Here are just a few of them:
    • Swagger generator
    • Distributed transaction
    • XA transaction
    • Channel
    • Reactive streams
    • 3rd party plugin library
    In my view the language is not production ready yet. I faced and reported a few issues during the dating, but it has great potential to be the first united Cloud Native language on the globe.

    Comments

    1. (disclaimer - I am a member of Ballerina evangelism team)

      Thanks Richárd for detailed review and your valuable feedback on Ballerina. As you spotted, our motivation is to improve user experience of developer to write modern cloud native programs.

      Ballerina has been inspired by Java, Go, C, C++, Rust, Haskell, Kotlin, Dart, TypeScript, JavaScript, Swift, and other languages. Its specialization is integration; it brings fundamental concepts, ideas, and tools of distributed system integration into the language and offers a type-safe, concurrent environment to implement such applications. These include distributed transactions, resiliency, concurrency, security, and container-management platforms.

      We have been working on Ballerina over more than 3 and half years, and recently we did the 0.990.0 release. The language specification and the current implementation has not reached 1.0 yet, but we are aggressively working on them. As you’ve also pointed out the current implementation has deviations from the language specification. We acknowledge these limitations you’ve come across, and we are actively fixing them.

      ReplyDelete

    Post a Comment

    Popular Posts