How to choose the best cloud platform for AI
Explore a strategy that shows you how to choose a cloud platform for your AI goals. Use Avenga’s Cloud Companion to speed up your decision-making.
We’ll share our perspectives on Go, the language of choice for some of Avenga’s innovative products.
In our other articles, we analyzed other promising languages, such as Julia, Rust, and Lua. Here, Jacek Chmiel, Director of Avenga Labs, delves into the Go language, exploring its purpose, applications, and the pros and cons that come with it.
Go is an open-source general-purpose language that is currently ranked 9th on the TIOBE index. Since its first release in 2012, Go has seen consistent updates, with significant releases twice a year, ensuring it remains relevant and efficient for modern development needs.
Primarily designed for Linux, Golang powers many of the most robust and well-known applications running on various Linux distributions. It also supports MacOS and Windows. However, some file system-related issues persist on the latter.
Go language was created to address common problems with the C and C++ languages when writing server-side services. It was designed for complex and reliable large projects utilizing multiprocessor and multicore architectures of modern data centers and workstations.
Go language is focused on code readability; it is designed in a way that makes it easy for large teams to work on projects together. It is always compiled for assembly and then for native binaries of the target operating system, which makes Go binaries very fast with no overhead of Just-In-Time compilers.
This language is statically compiled, and the current version of standard libraries and other dependencies are included. Of course, the price of that approach is the increased binary size, but in return, there’s a guarantee it will work. Unlike many other languages, there are almost no issues with library dependencies or version conflicts.
Very famous tools such as Docker, Kubernetes, Terraform, OpenShift, Cloudflare, Couchbase, InfluxDB, and Soundcloud are developed in the Go language.
→ Our take on Kubernetes dropping Docker runtime support – what it means for enterprises
Our entire cloud, including native cloud environments, runs on runtimes and tools written in Go code. There’s no doubt about its usefulness. It’s not an exotic language, and it’s a great tool.
Our own API management tools have been rewritten in the Go language, so there’s no question that it can also be used to manage and create various APIs (i.e., REST, gRPC). There are examples of Go applications for desktops using Qt and other GUI libraries. They are not very popular, but doable.
Applications that are written in such a fast and efficient language as Go can be compiled to webasm and executed in any modern browser.
The philosophy behind Go programming can be described as Keep It Simple. Anyone with a background in C and C-derived programming languages (C++, Java, C#) should be able to learn Go language quickly.
It’s not to say that Go isn’t very different from them, as it is. The main difference is a minimal set of language features from one side and features baked directly into the language (like collections, maps, concurrency, arrays, slices, etc.) instead of adding them to external libraries (contrary to most other languages).
Go comes with a great tutorial on its main webpage, which enables developers to discover language features interactively online. Microsoft Visual Studio’s code has excellent language support for Go with plugins, which are suggested automatically when opening .go files.
Discover how JasperLabs leveraged Avenga’s expertise to enhance its data processing capabilities. By partnering with Avenga, JasperLabs significantly reduced their processing time, enabling real-time analytics and setting new standards in data management. Success story
Figure 1. One of the Go source files from one of Avenga’s projects.
Here is a fragment of code from one of our products:
func TestHealth_ServeHTTP(t *testing.T) {
type fields struct {
path string
shutdownCh chan struct{}
}
tests := []struct {
name string
fields fields
req *http.Request
wantStatus int
}{
{"healthy check", fields{shutdownCh: make(chan struct{})}, httptest.NewRequest(http.MethodGet, "/", nil), http.StatusOK},
{"healthy check /w nil chan", fields{}, httptest.NewRequest(http.MethodGet, "/", nil), http.StatusOK},
{"unhealthy check", fields{shutdownCh: make(chan struct{})}, httptest.NewRequest(http.MethodGet, "/", nil), http.StatusInternalServerError},
}
for _, tc := range []testCase{
{"name", "user", "pass", "", "Basic", "", false},
{"name", "user", "", "", "Basic", "", false},
{"name", "", "pass", "", "Basic", "", false},
{"name", "", "", "", "Basic", "", false},
{"name", "user", "pass", "testdata/htpasswd", "Basic", "", false},
{"name", "john", "pass", "testdata/htpasswd", "Basic", "", false},
{"name", "user", "pass", "file", "Basic", "open file: no such file or directory", true},
{"name", "user", "pass", "testdata/htpasswd_err_invalid", "Basic", "basic auth ht parse error: invalidLine: testdata/htpasswd_err_invalid:1", true},
{"name", "user", "pass", "testdata/htpasswd_err_too_long", "Basic", "basic auth ht parse error: lineTooLong: testdata/htpasswd_err_too_long:1", true},
{"name", "user", "pass", "testdata/htpasswd_err_malformed", "Basic", `basic auth ht parse error: malformedPassword: testdata/htpasswd_err_malformed:1: user "foo"`, true},
{"name", "user", "pass", "testdata/htpasswd_err_multi", "Basic", `basic auth ht parse error: multipleUser: testdata/htpasswd_err_multi:2: "foo"`, true},
{"name", "user", "pass", "testdata/htpasswd_err_unsupported", "Basic", "basic auth ht parse error: notSupported: testdata/htpasswd_err_unsupported:1: unknown password algorithm", true},
} {
func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) {
return func(path string) ([]byte, error) {
client := &http.Client{Timeout: timeout}
req, err := http.NewRequest("GET", path, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
defer func() {
if resp != nil {
if e := resp.Body.Close(); e != nil {
log.Println(e)
}
}
}()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status)
}
return ioutil.ReadAll(resp.Body)
}
}
The Go compiler is quite strict and cautious, which is a pain for fast prototyping, but great for large projects.
For instance, if you declare variables and you don’t use them it’s a compiler error . . . not a warning, but an error!
func F() int {
var f = 5
return 0
}
Figure 2. The main features of Go
After installing Go, multiple elements are included out of the box: compiler, testing framework, build chain, lint for code formatting, and a static code analysis tool.
Command go get downloads external libraries by an URI; i.e.,
go get -u github.com/gogo/grpc-example
You can run the formatter to improve the code layout with the built-in command fmt
go fmt mysourcefile.go
On the other hand, developers have voiced their disappointment with the lack of a more advanced package management system such as npm (from node) or pip from Python.
Go has structs and functions, so no classes combine methods and encapsulate data. Go means no inheritance, polymorphism, virtual methods, object constructors, etc. However, simplicity and performance mean trade-offs in language capabilities and richness.
Types can be derived (not inherited) from base types, but only the value members will be there, not functions. There’s no such thing as methods inherited from the base type.
package main
import (
"fmt"
"strconv"
)
type person struct {
name string
birthYear int
socialID int
}
// function for Person type
func (p person) description() string {
return ("Name: " + p.name + " born " + strconv.Itoa(p.birthYear) + " socialID=" + strconv.Itoa(p.socialID))
}
// type almost equal, almost means no access to "description" function from "base type"
type employee person
func main() {
myPerson := person{name: "Jacek", birthYear: 1995, socialID: 23234234}
fmt.Println("Person data: ", myPerson.description())
var myEmployee employee
myEmployee.name = "Jacek"
myEmployee.birthYear = 1995
myEmployee.socialID = 782834
// will the method from other type work? NO! it won't compile
fmt.Println("Employee data: ", myEmployee.description())
}
Go is focused solely on composition. Types can only contain primitive members of their own and other types.
Types can be embedded, as the example below shows:
package main
import (
"fmt"
"strconv"
)
type person struct {
name string
birthYear int
socialID int
}
// this time we use composition of types
type employee struct {
person
employmentYear int
salary int
}
// function for Person type
func (p person) description() string {
return ("Name: " + p.name + " born " + strconv.Itoa(p.birthYear) + " socialID=" + strconv.Itoa(p.socialID))
}
func main() {
myPerson := person{name: "Jacek", birthYear: 1995, socialID: 23234234}
fmt.Println("Person data: ", myPerson.description())
var myEmployee employee
myEmployee.name = "Jacek"
myEmployee.birthYear = 1995
myEmployee.socialID = 782834
myEmployee.employmentYear = 2019
myEmployee.salary = 1100
// will the method from other types work? YES!
fmt.Println("Employee data: ", myEmployee.description())
}
Please note that there is no implements keyword equivalent known from other languages. There’s no keyword at all. Instead, duck typing is used to match the implicit interfaces.
Go is a statically typed language. While it includes reflection and some type of inference, it does not support prototypes or dynamic classes. Simplicity is a core principle of Go.
Go language is strongly criticized for lacking generics, but workarounds like empty interfaces exist. Addressing this issue is one of the major changes planned for future versions of Go.
Unfortunately, the eternal problem of no values and nulls is not fixed in Go. NULL issues apply to Go, which is sad and disappointing because the language is much younger than its NULL-infested predecessors.
Instead of an exception, there’s an error return type that carries the information about the improper execution of the routine. It requires writing code explicitly dealing with the errors, which could be annoying for newcomers, but it helps to maintain discipline because errors cannot be simply ignored. Infamous empty try catch finally, etc., do not exist here.
The Go developer community is approximately 13.5 percent of the global population of developers; they are experienced and active. Still, it’s harder to find information online compared to Python, Java, JavaScript or C#.
Avenga’s leadership highlights some critical reasons for choosing Go for product development. Go has a standard library covering HTTP, TLS, networking, and parsers. Those allow you to replace code clusters with a single line of code, making coding more lightweight and efficient. It’s a convenient and intelligent way of programming concurrency like C++ and Java, which is a viable benefit to leverage complex applications.
An interesting fact: two of the Go language co-inventors, Robert “Rob” C. Pike and Kenneth “Ken” L. Thompson, were a part of the original team that had developed Unix OS.
Looking further, it is versatile, and its ease of use and speed of the software development process, with simple functions-based syntax, are significant advantages of Go programming for server-side requirements. What is also worth mentioning is that Go applications are a suitable choice for a Docker container, as fat binaries allow for slim Docker scratch images. Although not without faults, being minimalist, clean, and readable makes it a language of choice for developers.
Avenga switched to Go language for all custom-built components, handling real-time USR traffic for one of our products. Go offers high performance and resource utilization on multi-core systems, especially for IO and network-bound tasks. The concurrency primitives give outstanding control over asynchronous processes. The standard library offers perfect support to programs on the network level and allows us to fine-tune the behavior of the HTTP and TLS stack. Last but not least, the built-in profiling tools helped us find bottlenecks and optimize.
Also, the Go language has been a natural choice for us when developing another Avenga product because it was designed for concurrent server processing, and the product is used in many docker-based customer setups. The small footprint of the Go binary offers well-controlled operations behavior.
There’s a world outside Java, Python, JavaScript, and C#. Go programming language is not expected to replace all those enterprise languages but it is an excellent choice when speed and a memory footprint are essential, and C/C++ complexities are to be avoided.
There is Rust, which also aims at simplifying things, but it is much more complex and harder to learn than Go. Go is more accessible and built with team collaboration in mind. Its code is readable and understandable. We appreciate its minimalism and letting go of almost all OOP legacy.
If you’d like to learn more about Go and how it could help achieve your business goals, contact us for a free consultation!
* US and Canada, exceptions apply
Ready to innovate your business?
We are! Let’s kick-off our journey to success!