This year, as a frontend engineer, I decided to challenge myself. Building my own backend service had always been a personal milestone. Earlier, I had successfully built one using TypeScript with tech stacks: Bun, Hono, Drizzle Orm, and PostgreSQL. But I wanted more—to learn a language closer to the metal. That’s how I ended up rewriting the entire thing in Go.
The first thing that hit me was Go’s error-handling approach: err != nil
everywhere. In JavaScript, error handling feels abstract—throw it, catch it, done. But Go demands explicit error checking after almost every operation. Initially, it felt cumbersome, but I started to appreciate its straightforwardness. It makes you confront potential failures head-on.
Then there were structs
and interfaces
. In TypeScript, interfaces define object shapes, and as long as your object fits, you’re golden. Go’s interfaces, however, define behavior. They’re implicit, which was a bit of a puzzle for me. And pointers? Let’s just say there were moments where I seriously doubted my ability to code. Managing memory directly is both fascinating and intimidating when coming from a JavaScript background.
Finally, there’s the ecosystem. JavaScript has this convenience of built-in methods and a rich library ecosystem. In Go, I had to implement things myself more often. Also, setting up the project structure in Go was a challenge. Unlike JavaScript, where community conventions are abundant, Go feels like a blank slate. After a lot of deliberation, I ended up mirroring my TypeScript project structure. It worked, but I kept wondering if it was "idiomatic Go."
The biggest challenge was understanding Go’s architectural patterns. Terms like "clean architecture" sounded great, but implementing them? That’s another story. I ended up sticking to what I knew from my TypeScript codebase. It’s functional but might not be the most Go-like approach.
Pointers and dereferencing were another hurdle. Coming from JavaScript, where memory management is abstracted away, dealing with pointers felt like navigating uncharted territory. When to use them, how they improve efficiency—it took a lot of trial and error to get the hang of it.
Lastly, the sheer volume of err != nil
checks sometimes felt excessive. While I understand and appreciate the clarity it brings, it definitely takes some getting used to.
Despite the challenges, the results were worth it. My Go-based service has a smaller memory footprint and uses fewer compute resources compared to its JavaScript predecessor. It’s leaner and faster. Plus, the process taught me a lot about Go’s architecture and best practices.
Writing in Go forced me to slow down and think deeply about every line of code. It’s not a language that hands you solutions on a platter—you have to earn it. But that’s also what makes it so rewarding.
Would I recommend Go to others? Absolutely. But be prepared for a learning curve. Go challenges you to think differently and write code with intention. If you’re up for the challenge, you’ll come out the other side with a deeper appreciation for both the language and the craft of programming.