They show:
- Clean layers
- Clear responsibilities
- Well-defined boundaries
Production bugs don't care.
They slip between layers, ignore boundaries, and surface in places your diagram never warned you about.
Let's see why โ with real code.
๐ผ๏ธ The Diagram Looks Perfect
Typical Spring Boot diagram:
Controller โ Service โ Repository โ DatabaseEach layer has one job. Each arrow flows in one direction.
Now let's meet production.
๐ Bug #1: The "Harmless" Shared Thread Pool
Diagram says:
"Each service is independent."
Code says otherwise:
@Bean
public Executor taskExecutor() {
return Executors.newFixedThreadPool(10);
}Used by:
- Async API calls
- Background jobs
- Event handlers
In production:
- One slow job blocks the pool
- API requests queue up
- Latency spikes everywhere
Bug location: Not in Controller. Not in Service. Not in Repository.
It lives between responsibilities โ invisible on the diagram.
โฑ๏ธ Bug #2: Time Is Missing From the Diagram
Diagram shows:
Service A โ Service BCode hides the real risk:
public User getUser(Long id) {
return restTemplate.getForObject(
"http://service-b/users/" + id,
User.class
);
}What the diagram doesn't show:
- How long Service B takes
- What happens if it slows down
- What happens under retry
In production:
- Service B slows
- Threads block
- Queues build
- Entire system freezes
The bug isn't logic. It's time.
๐ Bug #3: Hidden Coupling Through Configuration
Diagram says:
"Services are loosely coupled."
Code disagrees:
service.timeout.ms=5000Used everywhere:
Thread.sleep(timeout);One config change:
- Slows background jobs
- Delays HTTP calls
- Increases thread usage
Nothing changed in the diagram. Everything changed in production.
๐งช Bug #4: Retries Cross Architectural Boundaries
Looks safe:
@Retryable(maxAttempts = 3)
public Payment process() {
return gateway.charge();
}In production:
- Slow gateway โ retry
- Retry โ more load
- More load โ slower gateway
- Slower gateway โ more retries
Now:
- One service failure
- Becomes a system-wide incident
The diagram still shows one arrow.
Reality shows a feedback loop.
๐งฑ Bug #5: Data Isn't as Local as the Diagram Claims
Diagram:
Service A โ Its DatabaseCode:
@Data
@Entity
public class Order {
@ManyToOne
private User user;
}Suddenly:
- Lazy loading
- N+1 queries
- Cross-table locks
- Performance collapse under load
The bug appears in performance, not correctness.
Diagrams don't show query plans.
๐จ Why Incidents Feel "Weird"
During incidents, engineers say:
"This doesn't make sense."
That's because:
- Diagrams show intent
- Production shows execution
- Bugs live in execution paths
Especially where:
- Threads block
- Queues fill
- Timeouts cascade
- Load shifts behavior
๐ง What Senior Teams Learn (The Hard Way)
They stop asking:
"Does this match the architecture?"
They start asking:
"How does this behave under pressure?"
So they design for:
- Bounded queues
- Explicit timeouts
- Isolated thread pools
- Clear failure modes
- Observable behavior
๐งช How to Make Bugs Respect Reality (Not Diagrams)
Add guards in code:
.orTimeout(300, TimeUnit.MILLISECONDS)
.exceptionally(ex -> fallback())Limit blast radius:
executor.setQueueCapacity(50);Expose runtime truth:
@Timed(percentiles = {0.95, 0.99})
public void process() { }โจ Final Thought (This Sticks)
Architecture diagrams explain how systems should work.
Production bugs reveal how they actually work.
If your system only works when reality matches the diagram, it doesn't work.
The strongest systems are built for what diagrams leave out.
๐ฌ Medium Engagement Hook
What production bug completely ignored your "perfect" architecture diagram?