JS? TS? Bun? Go!

Marcin Sodkiewicz
5 min readSep 21, 2023

Case study of migrated Serverless solution from TypeScript to Go language. Based on 1-to-1 migration, facts, figures and reasons behind it.

Intro

More than six years ago, my team decided to migrate our products to a serverless architecture and adopt a serverless-first approach. At that time, all services managed by my team were developed in Java. Due to memory consumption and cold starts, we had to change the programming language.

Obviously it wasn’t an easy decision. There were heated discussions and arguments. Most of the discussions were about choosing between Node.js and Go.

Migration

For serverless applications based on AWS Lambda, we decided to use Go as our main language.

(If you are interested in our process and reasoning you can find it on the bottom of the article in the Appendix section).

At the same time, we implemented a project in Node.js, just to have a comparison.

Over the time we have added few more for applications that had better libraries or were better fit for the job #pdf #headlessbrowser

We recently received a lot of new requirements for our guinea pig serverless application based on Node.js. It felt like a good opportunity to migrate it to Go and bring it up to team standards. We have built a lot of common libraries, best practices and shared standards in building our Go based lambdas. It was considered technical debt recently and it was right thing to do.

This system is used on production, so first iteration was just a rewrite to another language without any configuration changes. Task was pretty easy, thanks to our e2e tests. We have used:

  • same Event Source Mapping configurations,
  • same lambda sizes,
  • same batch sizes,
  • same lambda architecture (arm64),
  • different runtime — nodejs16.x vs provided.al2.

These components are consumers in an event-driven system, processing events from SQS & Kinesis in large batches.

Okay, okay… Show me the data

The results are really satisfying. Especially that we haven’t made any additional improvements yet. We can see that durations & their standard deviations are much lower after migration. Fortunately, the number of invocations is more or less the same, so this analysis is pretty straightforward.

What is more — build times and package sizes have also become much smaller.

Component A:

Component A Duration
Component A Invocations

Component B:

Component B Duration
Component B Invocations

Pricing
Since AWS Lambda is priced based on GB-hours and we haven’t changed lambda size, but have lowered total invocation time what we can expect is that pricing will also be lower. This is exactly what happened. Was it revolution in our billing? Not really, yet it’s just another good thing about that migration.

Summary

I have always liked JavaScript and spent some time in my career focusing on the web and writing in React. I have written many of my private lambdas in TypeScript (such as lambdas processing data for https://www.aws-geek.com/), but I only used TypeScript as a means of not getting too rusty. Frankly, I have come to regret that choice.

Don’t get me wrong. Writing AWS lambdas in JavaScript and TypeScript has tons of advantages, and in many cases it can be a better choice than Go. It’s just a matter of choosing the right tool for the job, and that’s why we still have some JavaScript lambdas wherever they fit.

This migration felt like a pretty good case study to share, as it’s an apples-to-apples comparison of a real system rather than some fake benchmark processing based on sorting, and I hope you enjoyed reading it :)

Appendix
Making a decision

We had a lot of discussions about superiority of one language over the other. I am not a big fan of such discussions, as I believe that what really matters is the product and system architecture and design.

Ranking language features
To cut the discussions we ended up with listing features that we were looking for. Go ticked all boxes.

Learning curve
At the time I felt that Node.js would be a better fit for our team. Back in the days, switching to Go was cumbersome, especially if you were switching from writing services reactive way with Reactor (WebFlux). There are many libraries for JavaScript that allow developers to work with collections like Java streams and reactive libraries. I thought that training Java developers to work with TypeScript seemed pretty straightforward.

I was aware that this argument looks nice as a bullet point on a slide, but reality is that for majority of developers switching into other language is not a big deal. Especially if they have experienced developers around that can support their learning process.

Over time, this process has proved to be efficient and much simpler than we first thought. Apart from typical “errors… errors… !@!$@%^!….. I love error handling in go”

Memory efficiency
Huge advantage over Node.js. I was surprised when I have found out how much memory people do allocate for their lambdas. I thought that it looks more or less like in State of Serverless from 2020 that you can find here: https://www.datadoghq.com/state-of-serverless-2020/#7

I have realised that it has changed over years as we can see in State of Serverless by DataDog from 2022 (https://www.datadoghq.com/state-of-serverless/#5).

You know what? Not for us.

Although I am not sure if thesis presented in the report (image above) is true.

Last words
I was not sure about that decision at first. But after some time… What can I say? I was wrong and I’m grateful that we had engineers that introduced Go into our organization. Thank you!

How I can be so sure about that? I hear it from my team quite often :)

--

--