TypeScript & Go in duo for backend development
Choosing between languages for backend development can be a battle. In this article, our team of cloud developers and architects explain why they opt for the combination of TypeScript and Go in their backend projects. Discover the perks and risks of the two languages and decide for yourself which one fits your project.
Picking your language for backend development is a hard one to call. Scalability, speed and stability are crucial to take into account when choosing a language and runtime for a new microservice. Let’s make that choice a little easier for you. After building a backend platform with +/- 500k connected devices a day, here’s why our cloud developers typically choose between TypseScript with NodeJS and Go.
A look at TypeScript
Since its 1.0 release in 2014, TypeScript has risen to be one of the most popular JavaScript alternatives. As an extension of JavaScript, it boasts an extensive compile-time typing system, though a developer is free to turn off parts of the type checking as desired. For execution, TypeScript is traditionally transpiled to plain JavaScript and runs in any JavaScript-compatible runtime environment, such as NodeJS or a web browser.
As JavaScript is one of the (if not THE) most popular programming languages of the last couple of years, it has a massive adoption rate, community and ecosystem. The standard library of NodeJS is fairly extensive and there is a large number of well-maintained third-party libraries and tools. TypeScript has full compatibility with the JavaScript ecosystem and over the years has been fairly well integrated into most major libraries. This, along with the fact that it is easy to get started with, makes TypeScript a solid choice for both backend and frontend development.
One of the drawbacks of TypeScript is that it is technically an interpreted scripting language that is evaluated in a runtime environment and not natively executed. This makes execution speed often pale in comparison to natively compiled programming languages which can be important when handling computation-intensive tasks.
Working with Go
As mentioned on its website, Go is an open-source programming language supported by Google that aims to be easy to learn and get started with. It is a natively compiled, low-level programming language, but it aims to keep things simple. It has a static typing system with strict runtime memory allocation but the language has a fairly small set of control flow structures and is very opinionated on how to write idiomatic code. An example of this would be, where other languages use `for`, `while` and/or `forEach`, Go just has `for`. Correctly written Go code may seem verbose, but is very easy to read and understand.
The standard library of Go has the necessary tools you would expect of a full-fledged programming language. The project setup is straightforward and well-documented. Since Go is opinionated about code idioms, it has its own linter, formatter, static code checker... These are maintained by the language maintainers themselves. There are a fair amount of third-party libraries and tools available. SAAS solutions and commercial products most always offer Go client libraries where applicable.
As Go is compiled to a native binary, execution is very fast and memory efficient. Go treats concurrency as a first-order citizen: it is baked into the language and easy to get started with and comprehend. This makes Go an ideal choice when efficiency matters.
Two sides of the same coin?
Anyone with a taste for software development might already know that these languages can both be used to solve the same problem, but there are nuances to each.
Both languages are typed, though the typing of TypeScript is removed at transpile-time when it is converted to plain JavaScript. Go uses its typing to allocate memory at runtime, so the typing system is used both for static code checking and runtime safety. And they both have a fairly complete standard library available, a solid array of unit- and end-to-end testing tools and serve as a good base for a cloud microservice.
So, what are the main trade-offs?
Runtime efficiency vs. getting it done quickly
First and foremost, a lot depends on the developers that are available. If your team is composed mostly or exclusively of developers that are not comfortable with one of the languages, this might cause problems with development speed, code quality and even maintainability in the long run.
If your team can handle both languages, the main question is: how important is runtime efficiency compared to “getting it done quickly”? Between the two, Go excels at speed, memory efficiency and binary size. Even though this makes Go sound tempting, an experienced TypeScript developer might write a piece of code in ten lines, while the equivalent Go code could be over a hundred lines long.
Using two languages: the risks and rewards
Second, using multiple languages for the same cloud platform carries its own risks and rewards. A downside is that your development team will need more time to learn and maintain language knowledge.
Code sharing between services is also a big hurdle to solve, and will mostly involve the conversion of a common specification to the target languages. On the other hand, a big advantage is that a developer and even an architect are “forced” to think more about the domain and less about the specific implementation of the problem at hand.
It is then up to the team to select the right tool for the job instead of always having to work in the same runtime. A developer might also personally enjoy the challenge of working with multiple languages and learning new language idioms.
Proven by example
Recently, we’ve been looking into one of our smaller services, which was originally written in TypeScript. This allowed us to get it working and deployed very quickly. And to date, it still does the job adequately for the current system load.
While load-testing different parts of the cloud platform, we noticed that the service quickly started performing worse under extreme but realistic loads. We decided that, as scalability is important for this specific service, we should look into a reimplementation in Go, as it seemed the better tool for the job.
Conclusion
We have discussed why and when we use TypeScript and Go for our microservice implementations. Both boast an active community, are not overly complex or specific to a domain and are generally supported by Cloud and SAAS providers.
Having the development team learn more than one language allows them to reason about problems in a more flexible way, and not only implementation-oriented.
We could write all our services in one of these languages, but now the team has the freedom to choose the right tool for the job: To get up and running quickly, TypeScript is ideal. When Runtime efficiency is important, Go can come to the rescue.