The Anomaly of the Shared Production Database
When scaling systems early on, operational efficiency often takes precedence over strict adherence to architectural constraints. In my experience managing core backend applications, PostgreSQL instances hosted on Google Cloud Platform served as the database engine. This database environment provided persistent storage for both low-latency transaction queries and the long-running analytical queries required by internal operations teams.
Initial development speed was high because engineers could write direct SQL queries against any combination of tables to retrieve metrics. Over time, background reporting processes began to directly compete with production applications. The structural flaw of this design became apparent when resource-intensive analytical queries caused row locks and resource contention, leading to daily connection pool exhaustion. The single PostgreSQL database went from being a high-performance storage solution to a major point of failure, leading to increased error rates for end users, while engineers spent time resetting database connections rather than writing software.
The Misconception of Microservices Adoption
The standard industry recommendation for resolving database access contention is to isolate the resource-intensive reporting module into a separate microservice. When evaluating this proposal, the short-term benefit of workload isolation was offset by significant operational and cost implications.
Creating a microservice for reporting without clearly defined business domain logic would have required duplicating significant portions of the core code for processing data input and object mapping. Running isolated network services on Google Cloud Platform would have increased ongoing infrastructure costs. Since our team operated without dedicated DevOps staff, the burden of maintenance—including continuous integration pipelines, infrastructure provisioning, and distributed monitoring—fell entirely on the shoulders of application developers. Implementing a distributed microservices architecture would have diverted limited developer resources from core product functionality to infrastructure management.
Creation of a modular monolith
Instead of complicating the distributed network, I decided to refactor the application into a modular monolith. The architectural goal was to ensure strict domain boundaries at the application level while maintaining a single deployable artifact and data store.
We reorganized the application code, dividing it into separate modules and establishing strict data access rules. Direct cross-table joins between non-isolated domain entities were explicitly prohibited in application queries. Inter-module communication was forced to use specific internal interfaces rather than direct database references. By refactoring the underlying data structures, we eliminated unnecessary query complexity and isolated areas with high memory utilization within the application.
Intentional architecture compromises
Choosing a modular monolith over a distributed network of services was a deliberate decision aimed at aligning the system’s complexity with the available engineering resources. We accepted the limitation of logic execution within a single process to maintain predictability and control cloud infrastructure costs.
Refactoring data access paths required a temporary reduction in the timeline for new feature implementations, as developers spent time restructuring database schemas and removing implicit cross-domain queries. This deliberate compromise ensured long-term stability. As a result, the codebase remained concise, easily understandable, and easy to maintain, without requiring increased engineering staff or large cloud budgets.
A shared database rarely becomes an initial bottleneck for hardware performance. It functions primarily as an organizational constraint, constraining deployment schedules and engineer autonomy.
Does this updated version align with the strategic perspective you want to give your blog, or would you like to add a section detailing the specific metrics you tracked to assess database load during the migration?
