When Architecture Becomes Overkill: Real‑World Lessons on Over‑Design
The article recounts a series of real‑world anecdotes exposing how excessive architectural ambitions—such as needless microservice fragmentation, ubiquitous message‑queue usage, multi‑level caching, over‑engineered design patterns, and configuration‑driven code—lead to wasted resources, maintenance headaches, and fragile systems, urging a return to simplicity.
Microservice Granularity Over‑Design
Some teams split a monolithic application not only by business domain but also by each controller, service and DAO class. The result is a large number of tiny services that communicate only to reuse a few methods. This violates the common “three‑service principles” (single responsibility, high cohesion, low coupling) and creates unnecessary network overhead, deployment complexity, and operational cost.
Middle‑Platform (Big Platform, Small Front‑End) Proliferation
Since 2015 a major cloud provider promoted a “big middle platform, small front‑end” architecture. Companies adopted it to centralise shared services (e.g., authentication, data‑processing) and accelerate front‑end iteration. In practice the responsibility boundaries of the platform are often debated without clear metrics, leading to duplicated effort and delayed delivery.
Ubiquitous and Misused Message Queues
Message queues (MQ) are praised for async processing, peak‑shaving and decoupling. However, they are frequently used for scenarios that do not require asynchronous messaging:
Sending an SMS verification code where the producer and consumer belong to the same service, merely to wrap a synchronous SDK call.
Logging by publishing a log entry to an MQ that is immediately consumed by the same service.
Low‑TPS tasks (e.g., a few orders per second) that are routed through MQ for “peak‑shaving”.
Inter‑service RPC calls replaced by MQ, inflating latency and operational complexity.
In many projects more than 80% of inter‑service communication is implemented with MQ, even when a simple HTTP/RPC call would be sufficient.
Multi‑Level Caching (Guava Cache + Redis)
A common pattern is to place an in‑process Guava Cache in front of a distributed Redis cache. This can reduce database load and improve response time, but aggressive caching leads to frequent data‑inconsistency problems:
Stale data in the local cache after a database update.
Cache‑invalidation storms when bulk updates occur.
Complex cache‑key management across multiple services.
Systems that adopt this pattern across OA, website, messaging, settlement, supply‑chain and CRM modules often experience hard‑to‑debug consistency bugs.
Design‑Pattern Overkill
In several code reviews, developers were forced to wrap trivial logic with heavyweight patterns:
Using the Builder pattern to construct a simple DTO.
Replacing a single if‑else with Strategy, Abstract Factory, Proxy or Observer patterns.
Insisting that every conditional constant (e.g., status codes) be externalised to a configuration centre.
While patterns are valuable, applying them indiscriminately increases code complexity, reduces readability and hampers maintenance.
Database Portability Practices
To minimise migration risk between MySQL, Oracle or other RDBMS, the following guidelines are recommended:
Use Hibernate HQL or JPA Criteria API to abstract vendor‑specific pagination ( LIMIT vs ROWNUM).
Avoid stored procedures; keep business logic in the application layer.
Prefer standard SQL (ANSI‑92) for DML/DQL statements.
Redundant Data Validation (Double‑Validation)
Some teams validate input at the API layer and then repeat the same checks after reading from the database. This “double‑validation” assumes that data could be altered outside the application, but it adds unnecessary processing overhead. A more robust approach is to enforce data integrity at the database level (constraints, foreign keys) and perform validation only at the boundaries where untrusted data enters the system.
Configuration‑Centric Code
Externalising every numeric or enum constant (e.g., map size thresholds, gender flags, product status codes) to a dynamic configuration centre enables runtime changes without redeployment. However, this practice can obscure business rules, make the codebase harder to understand, and increase the risk of configuration drift.
Summary
The examples above illustrate a recurring pattern: senior engineers often introduce architectural “bells and whistles”—excessive microservice granularity, heavyweight middle‑platforms, over‑reliance on message queues, multi‑level caches, unnecessary design patterns, strict configuration externalisation, and redundant validation. While each technique has legitimate use cases, applying them indiscriminately leads to higher operational cost, increased system fragility, and harder maintenance. Adopting a pragmatic “keep it simple” mindset, evaluating trade‑offs with concrete metrics (latency, throughput, resource utilisation), and favouring straightforward solutions where appropriate can dramatically improve system reliability and developer productivity.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Senior Tony
Former senior tech manager at Meituan, ex‑tech director at New Oriental, with experience at JD.com and Qunar; specializes in Java interview coaching and regularly shares hardcore technical content. Runs a video channel of the same name.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
