There’s a temptation, when you’re building software for large organizations, to let the complexity of the customer’s world bleed into the complexity of what you build. The customer has ten business units with different billing requirements. The customer has regulatory constraints that vary by geography. The customer has legacy systems that need to connect to your new thing in ways that weren’t designed to work together. All of this is real. All of it creates pressure to build something that accommodates every edge case, every workflow variation, every exception to the rule.
The trap is that complexity can look like thoroughness. A product with more configuration options looks like it was designed by people who really thought about the problem. A system with more layers of abstraction looks sophisticated. Feature lists that address more use cases look more complete. From the inside of the team building it, complexity often feels like effort made visible.
What I’ve come to believe, pretty firmly, is that complexity is usually a sign of unresolved thinking, not resolved thinking. The simple thing is almost always harder to build because it requires you to make decisions that the complex thing defers.
Where complexity creeps in
At Capital One, the org I was leading built data and analytics products that had to work across a large, heterogeneous organization — different lines of business, different data sources, different consumption patterns. The surface area for “well, but what about this case” was enormous. And every time someone asked that question, the path of least resistance was to add a configuration option, a workflow branch, an additional parameter that the user could set.
Each individual decision made sense. The accumulation of them produced something that was genuinely hard to understand, hard to onboard onto, and hard to operate. The complexity didn’t arrive all at once — it accrued, one reasonable-seeming exception at a time.
The specific pressure points I’ve seen most often: first, when business stakeholders have competing requirements and the engineering team resolves the conflict by building something that satisfies both rather than making a call about which one to prioritize. Second, when the team lacks confidence in a design decision and compensates by making everything configurable, so that the user can sort it out. Third, when previous attempts at simplicity got broken by edge cases, so the team’s default posture is to over-specify up front.
All three of these are management failures, not engineering failures. They come from not creating enough clarity about what the product is actually for and who it’s actually for.
The moment the simpler path won
One specific decision I remember from my Capital One days involved a reporting product that was supposed to help internal teams track specific performance metrics. We had a proposal on the table for a highly configurable dashboard system — users could define their own metrics, their own views, their own alert thresholds. The pitch was that this flexibility would make the product useful to more teams and require less maintenance from us, since teams could self-serve.
The counterproposal was simpler: build opinionated dashboards for the three or four use cases we actually knew about, make those dashboards excellent, and revisit configurability once we understood the real breadth of need.
There was real internal pressure against the simpler path. The configurable system felt more ambitious. It felt like it would serve more people. And the simpler system meant committing to specific use cases before we’d heard from all the stakeholders who might want something different.
I pushed for the simpler path, against some internal resistance. What happened: adoption was faster than projected, because the opinionated design made it obvious how to use the product. The edge cases people had theorized about mostly didn’t materialize. When a new use case did come up that we hadn’t anticipated, we were able to add it without the complexity overhead of a fully general configuration system.
The configurable version might have worked too. But the simpler version worked for certain, and it let us learn what people actually needed rather than what we imagined they might need.
What this means in practice
When I’m reviewing a product or system design now, one of the questions I ask most often is: “What decision are we deferring by making this configurable?” Configuration is often a way of not deciding. Which is fine sometimes — genuine variability exists and you should accommodate it. But a lot of what gets built as configuration is actually just an unmade design decision, and unmade design decisions have a way of showing up later in support tickets, in onboarding friction, in maintenance costs.
Simplicity in enterprise products isn’t about ignoring complexity. It’s about absorbing complexity inside the system so the person using it doesn’t have to. That’s the discipline — understanding the full complexity of the problem well enough to hide it from the user without pretending it doesn’t exist.
It’s harder than adding a configuration option. That’s why it’s the right thing to do.
Further Reading
- Containers, Microservices, and the Monolith Nobody Admits They Have
- How I Think About Technical Debt as a Business Tradeoff
- The Relationship Between Cloud Architecture and Business Agility
- Making Architectural Decisions Without Being the Smartest Person in the Room
- Infrastructure Is Never Just Infrastructure