Relationship with JavaScript over. I'm in love with Go now.

As a full-stack web developer, I strive to make my code clean and efficient, but ultimately, faster. For the majority of my web development journey, I've been using most of the "hypebeast" stack, as Ben Awad would say. My go-to web development stack was:

  • Next.JS (frontend)
  • Express.JS (API framework)
  • Node.JS (API)
  • MongoDB (long-term storage database)
  • Redis (short-lived memory database)

JavaScript Flaws

These languages and frameworks are great for web development, but I wanted more performance and reliability. JavaScript has a few flaws that made me move away:

1. It's easy to make mistakes.

Dynamically typed languages will say :) no matter what, making it harder for developers to catch errors during development, rather than in production where things can go catastrophically wrong. In statically typed languages, like Go, the compiler will scream at you with a five-page long MLA formatted essay on why you messed up and how to fix it.

JavaScript not caring about errors and mistakes

I learned this the hard way in my project Horizon. Before I rewrote the API in Go, my users often reported short-lived but critical outages where the API crashed. Luckily, I use auto-restart on all of Horizon's infrastructure and micro-services. However, it's still a significant inconvenience for the unlucky users who happen to try to access the service while it's being restarted. Ever since the rewrite, things have been smooth. I've been able to catch 95% of errors in my IDE during development rather than debugging in production. As much as people love to joke about debugging in production, please... don't do it.

Now, of course, there's TypeScript, and it does a great job at what it does. It provides powerful linting tools and feedback, but it's still compiling to JavaScript, which does not take care of the following points.

2. It's slow

Interpreted languages are slower by design than compiled languages. Instead of having a binary where all the code is compiled and easily accessible, interpreted languages have to pass all code through an interpreter, which processes the code, converts it to the low-level base language, and executes. This process is done repeatedly, creating a significant time overhead over time. This can also mean worse performance during peak usage, like a layer 7 DDoS attack or many users accessing your service in general.

Horizon has been a victim of many DDoS attacks throughout its lifetime. Before switching to Go, its Node.JS API always crashed and suffered during these attacks. With Go, it has been the exact opposite. Go can handle thousands of requests per second without crashing or showing any sign of weakness or performance degradation.

Go and other statically typed languages have compilers that can scan the whole program, check for errors (see the last point above) and translate the human-written code into machine code that can be executed by the server processor. This makes it a lot more efficient during runtime because there is no interpreter. The "interpretation" stage is done all at once, eliminating the need to do it during runtime. However, unlike other languages, Go's compiler is blazingly fast, saving developers time when debugging.

3. Virtually no concurrency

JavaScript and Node run on a single thread technically. While you can use Node clusters, they still won't even come close to the amount of concurrency freedom you have with Go. Go has these things called "goroutines", which essentially allows you to branch off a function to another lightweight and managed thread that doesn't block your other asynchronous code. For example, when a user requests a restaurant's menu, the restaurant can reply to the user with the menu data and update the number of visitors in the database concurrently in a goroutine. This makes it, so the database update function has a minimal impact on response time. It'll still be done, just on the side where it can't have an actual impact speed or performance.

In JavaScript, there's async/await thanks to es6; however, these often provide the illusion of concurrency. Unlike Go, you can't as easily spawn a new thread to do some code on the side. The program will still wait for the async/await operation to complete.

Conclusion

Before moving to Go, I used to hate on the language. I often criticized its quirky syntax, but I've come to love it. Its enhanced performance and various concurrency features have attracted me the most to the language. As of now, my development stack is:

  • Next.JS (frontend)
  • Go-Gin (API framework)
  • Go (API)
  • MongoDB (long-term storage database)
  • Redis (short-lived memory database)