Migration Journey: From Firebase to Go, PostgreSQL, and MinIO Stack - Vol. 1
Introduction
For any growing application, the choice of backend architecture is a critical decision that impacts scalability, cost, and feature velocity. Our AI-driven health tech platform, initially built on a Flutter application with a Firebase backend, reached an inflection point. While Firebase enabled rapid initial development, we identified strategic limitations that would hinder our long-term goals, particularly with a major US launch on the horizon...
This article details our journey of migrating to a more robust and flexible architecture: a Go backend API server, a PostgreSQL database, and a self-hosted MinIO object storage solution. We will cover the strategic rationale, key implementation practices, and a deep-dive into a complex debugging session that became a microcosm of the migration's challenges.
The Strategic Imperative: Why We Moved Beyond Firebase
Anticipating Scalability and Cost
Our "Fecal Scanner" application started as a simple service: scan your stool, get an analysis. However, our roadmap for the US market is far more ambitious. We envision a transformation from a simple analysis tool to a comprehensive bowel health platform, incorporating features like diet analysis. This expansion naturally leads to a significant increase in data and file objects.
Firebase, while excellent for rapid prototyping, presents cost challenges at scale. The pricing model, which may seem insignificant initially, can become a substantial expenditure as data and user load grow. This projected cost, coupled with the need for a more scalable solution, was a primary driver for our migration.
Enabling Future Flexibility
The planned evolution of our application requires a backend capable of handling complex business logic. While Firebase's Cloud Functions and Firestore security rules offer some flexibility, they can become cumbersome when managing a large number of small, serverless functions. Serverless architectures also have inherent limitations in terms of execution duration and memory, which could have become a bottleneck for our more computationally intensive features. We needed a backend that would empower, not hinder, our future development.
Choosing Our New Stack: Go and PostgreSQL
Why Go for the Backend?
The decision to use Go for our backend was a deliberate one, driven by a desire for performance, simplicity, and efficient concurrency. In a landscape often dominated by Java and JavaScript in Korea, Go has emerged as a powerful choice for modern backend development. Here's why:
- Simple and Clear Syntax: Go's syntax is intentionally simple, which can lead to more readable and maintainable code. While some critics argue this simplicity comes at the cost of some advanced language features, we found it to be a significant advantage in a team environment.
- Effortless Concurrency: Go's built-in support for concurrency through "goroutines" is a game-changer for backend applications. Goroutines are lightweight threads managed by the Go runtime, and a single program can run thousands of them concurrently. The official
net/http
package in Go even launches a new goroutine for each incoming request, ensuring high performance even with a small number of application instances. - Impressive Performance: Go's performance is a well-documented advantage. It compiles to machine code, resulting in faster execution times compared to interpreted languages. In a benchmark comparison of server-side I/O performance, Go consistently outperformed Node.js, PHP, and even Java in handling a high volume of concurrent requests. For instance, in a test with 5,000 concurrent requests, Go handled the highest number of requests per second. More recent benchmarks in 2024 continue to highlight Go's strong performance, often placing it ahead of or on par with other high-performance languages like Rust.
Why PostgreSQL?
The choice of PostgreSQL was a straightforward one. We needed a relational database with a rich feature set to avoid relying on additional services for functionalities like full-text search. PostgreSQL is renowned for its extensive features, supported by a vibrant ecosystem of libraries and extensions. Its performance has also seen significant improvements over the years, often outperforming other popular open-source databases. This combination of features, performance, and scalability made PostgreSQL the ideal choice for our new architecture.
Wrapping Up Volume 1: The "Why" Before the "How"
This first post has laid out the strategic thinking behind our decision to move away from Firebase and embrace a new stack composed of Go, PostgreSQL, and MinIO. We've explored the critical factors of scalability, cost, and long-term flexibility that drove this architectural shift. By establishing the "why," we have set the stage for the more technical aspects of our migration.
In the next volume of this series, we will transition from strategy to execution. We'll dive into the "how" of our journey, covering the initial setup of our new infrastructure, key architectural patterns we implemented in Go, and the inevitable challenges we faced—and overcame—during the development process.
Stay tuned as we continue to share the lessons learned from building a scalable and resilient backend for our growing health tech platform.