I’ve been a Ruby developer for over 10 years. The only other language I’ve properly adopted is Swift. The bulk of which I learned while developing my iOS app Hazumi News, with Ruby on Rails on the backend. I read a lot of books on architecture and programming, even listen to them on Audible sometimes. You might come away from those kinds of books feeling like you can rewrite your apps in <shiny new language> and be better off. I was bitten by that bug many times in my life and was bitten again by the Golang bug in my 9 to 5.
Structured learning works best for me, so I bought the second edition of Learning Go from O’Reilly Media and studied it. I also listened some podcasts regularly, shout out to Cup o’ Go and Go Time.
Programming with Golang was great. It was very clear what the code was doing, it was oh-so fast, and thanks to its built in formatter fmt, my code looked just like everyone else’s. I learned to write functions I’d taken for granted in activesupport from scratch and it was humbling.
I enjoyed Golang so much that I decided to rewrite my backend for Hazumi News to really learn it and see what it could do in production first hand. Very rarely should you rewrite an entire application unless you have good reason to do so. This was a small side project so I had nothing to lose. I quickly diagrammed what I needed to focus on and got to work.
In Hazumi News, I was running Sidekiq with Redis to manage asynchronous jobs. The bulk of Hazumi’s backend operates by running data-fetching tasks on a regular basis. Every 10 minutes to an hour, data is pulled from Hacker News, parsed, saved, cached and summarised while sending push notifications to relevant subscribers.
Things went slow but steady, but quickly grew more and more complex. I was writing multiple lines of Golang code that would only take one method call in Ruby. I didn’t want to use any dependencies either, because the standard library was great and I enjoyed the challenge. I was coding a simple scheduler from scratch too, do X every Y and then progress began to slow to a crawl.
As my Golang skills improved, I started to go back and find new logical boundaries to refactor and moved code into their own packages. It reached a point where I even split the application into five microservices (yep, I know), using HTTP for communication between them. Each service with its own mappers, models, and services folder. That’s when progress really slowed to a crawl.
I tried to tame the madness. I set up Docker to start each service in the correct order, configured the internal network for each app to talk to each other. Then I took some time to make sure I could successfully deploy these services to Fly.io and have a similar internal network system. It worked well. I checked Grafana, and was pleasantly surprised that each service was only using around 70-90MB and response times were really fast. The smallest service I could spin up in Fly for each app was 256MB of RAM so they had plenty of overhead. My Rails application for comparison, by default, requires at least 512MB per Puma process.
However, pace never improved, It got worse. I struggled to keep everything in my head. There were so many similarly named models and functionality just for inter service communication that I couldn’t keep track of it all. I decided that was enough. I spent nearly two months chasing parity with my Rails application and ultimately failed. I didn’t relish throwing away a few thousand lines of code but it was for the best. It was around this time that Rails World was happening and I was reading all about the amazing new features coming to Rails 8. It was like a calling.
Looking back, I learned so much on this little adventure that I believe was really worth it in the end. I still love Golang, but for my solo endeavours, and any application bigger than a single microservice, I will stick to the majestic monolith.