Last week, I sat in yet another architecture meeting where someone proposed splitting our application into a constellation of microservices, each with its own database, connected by an event bus. When I asked what problem we were solving, the response was: “It’s the modern way to build systems.”
This is not architecture. This is cargo culting.
Over two decades building software systems, I’ve learned a hard truth: adopting distributed patterns because they’re trendy is not engineering — it’s resume-driven development.
The Problem with “Best Practices”
Somewhere along the way, the industry decided that microservices and event-driven architectures are inherently superior to other approaches. Conference talks showcase elaborate system diagrams. Tech blogs celebrate migrations from “legacy monoliths” to “modern architectures.” Senior engineers list “microservices” on their LinkedIn profiles like it’s a badge of honor.
But here’s what nobody wants to admit: most systems don’t need this complexity, and forcing it creates more problems than it solves.
What Architecture Actually Means
Architecture is not about choosing trendy patterns. It’s about:
1. Understanding the problem you’re solving
2. Evaluating trade-offs honestly
3. Choosing the simplest solution that meets your actual requirements
4. Being able to articulate why you made specific decisions
When someone proposes microservices, they should be able to answer:
• What specific technical constraint are we addressing?
• What are the measurable benefits?
• What are the operational costs?
• What are the failure modes?
• How will we verify this improves things?
If the answer is “because that’s how modern systems are built,” that’s not architecture. That’s fashion.
The Hidden Costs of Premature Distribution
Operational Complexity Explosion
Splitting one system into N services means:
• N databases to backup, restore, and monitor
• N deployment pipelines
• N sets of logs to aggregate
• N performance profiles to understand
• N sources of potential failure
• N² integration points to test
This isn’t theoretical overhead. This is real engineering time that could be spent solving actual business problems.
The Debugging Tax
In a single application, debugging means:
• Check logs
• Add breakpoints
• Trace execution
• Find root cause
• Fix issue
In distributed systems, debugging means:
• Aggregate logs from multiple services
• Correlate timestamps across systems
• Trace request IDs through message queues
• Determine which service has “correct” state
• Handle cases where services have inconsistent views
• Question life choices
I’ve spent entire weekends debugging issues that would have been 10-minute fixes in a simpler architecture.
Data Consistency Becomes Your Problem
When you split databases across services, you give up transactional guarantees. Now you’re responsible for:
• Eventual consistency (which can mean milliseconds or hours)
• Event replay when consumers fail
• Reconciliation when systems diverge
• Handling partial failures gracefully
• Explaining to users why their data doesn’t match across screens
Teams end up building elaborate synchronization mechanisms to recreate what databases have provided for decades.
Feature Velocity Collapses
Simple operations become distributed systems problems:
Before: Query multiple related entities in one transaction
After: Coordinate calls across services, handle timeouts, implement retries, cache aggressively, debug why results don’t match
What used to be a simple database query becomes an orchestration challenge. Teams stop shipping features and start building infrastructure.
The Event-Driven Cargo Cult
Event-driven architecture is powerful **when you need it**. But treating every operation as an event is like using a sledgehammer for finishing nails.
Valid reasons for events:
• Integrating independent systems you don’t control
• Fan-out to multiple external consumers
• Genuinely asynchronous processing
• Different components with drastically different scaling needs
Invalid reasons for events:
• “It’s more scalable” (than what? where’s the bottleneck?)
• “It enables loose coupling” (so do interfaces and abstractions)
• “It’s what successful companies do” (they had different problems than you)
• “It’s best practice” (best for whom? in what context?)
I’ve reviewed systems where teams published events for operations within the same application that could have been function calls. The result? Three additional AWS services, eventual consistency headaches, and debugging nightmares — all to avoid calling a function.
The Questions Engineers Should Ask
Before adopting any architectural pattern, demand answers:
1. What Problem Are We Solving?
Not theoretical concerns. Not “might need to scale someday.” Actual, measurable problems:
• “Query latency exceeds 500ms for 20% of requests despite optimization”
• “Database CPU consistently at 90% despite proper indexing”
• “Teams cannot deploy independently, causing coordination overhead”
• “Different components require different programming languages due to specific libraries”
If you can’t articulate the specific problem, you’re not doing architecture.
2. What Are the Alternatives?
Before jumping to distribution, consider:
• Better indexing strategies
• Read replicas for query workload
• Caching layers
• Vertical scaling (shockingly effective and often cheaper)
• Code optimization
• Query optimization
• Connection pooling
• Partitioning strategies
Most performance problems can be solved without distributing the system.
3. What Are We Giving Up?
Every architectural decision involves trade-offs. When you split systems, you sacrifice:
• Transactional consistency
• Simple debugging
• Easy testing
• Straightforward deployment
• Low operational overhead
• Fast development velocity
Are you willing to pay these costs? Can you even afford them?
4. How Will We Measure Success?
Define concrete metrics:
• Query latency targets
• Deployment frequency goals
• Mean time to recovery
• Development velocity (story points per sprint)
• Infrastructure costs
• On-call incidents
If you can’t measure whether the new architecture improved things, you’re just hoping.
5. What’s the Rollback Plan?
If this makes things worse, can you revert? Or are you committed to making it work regardless of cost?
The Responsibility of Senior Engineers
As experienced engineers, we have a professional responsibility to push back on complexity theater.
It’s tempting to chase the latest patterns. To want impressive architecture on our profiles. To build systems that look good on conference slides.
But our job is not to build impressive architecture. Our job is to solve business problems reliably, maintainably, and cost-effectively.
Sometimes that means saying no to the shiny pattern. Sometimes it means defending simpler approaches. Sometimes it means being the person who asks “but why?”
What Good Architecture Looks Like
Good architecture:
• Solves actual problems, not theoretical ones
• Minimizes complexity while meeting requirements
• Can be explained clearly to both engineers and stakeholders
• Has measurable benefits over alternatives
• Considers operational reality, not just design elegance
• Evolves incrementally as needs change
Good architects can articulate:
• Why they chose this approach
• What alternatives they considered
• What trade-offs they accepted
• What metrics prove it’s working
• What would cause them to change direction
The Industry Problem
We’ve created a culture where:
• Microservices are assumed superior without evidence
• Event-driven is treated as inherently better than request-response
• Monoliths are considered legacy by definition
• Distributed systems are default, not justified
This is not engineering. This is fashion.
The industry needs to recover the ability to say: “This simpler approach solves our actual problem better.”
A Different Perspective
What if we evaluated architectures based on:
• Time to ship features
• Mean time to recovery from failures
• Developer onboarding time
• Operational overhead
• Total cost of ownership
• Actual (not theoretical) performance
Instead of:
• How many services we have
• Whether we use Kafka
• How “cloud-native” the diagram looks
• Whether it uses the latest patterns
The Bottom Line
Microservices, event-driven architectures, and distributed systems are tools. Powerful tools. Like any powerful tool, they cause tremendous damage when applied incorrectly.
Splitting systems should be a last resort, not a first instinct.
Before you distribute:
• Prove you need to with metrics
• Understand the full costs
• Evaluate simpler alternatives
• Define success criteria
• Plan for failure
And remember: architecture based on trends is not architecture. It’s theater.
The best architecture is the one that solves your actual problems with minimum complexity. That’s rarely the trendiest approach, but it’s usually the right one.
Member discussion: