The Duality of Problems and Solutions in Product Engineering
Every solution carries the seeds of its next problem. A product engineer's perspective on why problems never stay solved — and why that's the point.
One of the things that has become crystal clear to me as I’ve gained experience in product engineering — and in life in general — is the profound duality of problem and solution. I used to think these two were polar opposites. That every problem had a solution, and once found, the problem would simply vanish. It was a naive, linear way of thinking that, after a good number of scars from real projects, I’ve come to understand is fundamentally wrong.
The two are intimately related — more like two sides of the same coin than two ends of a spectrum. Have you noticed how within every problem lies a solution? And that every solution arrives carrying a new set of problems? You find yourself on one side, you work to reach the other, only to start dealing with a new set of challenges the solution brought with it — often more sophisticated, sometimes more subtle, occasionally more interesting than the original.
Problem Migration: The Engineering Reality
Let’s think about the invention of the wheel for a second: it brilliantly solved the problem of transportation over flat terrain. Yet, the wheel then created the problems of roads, axles, bearings, speed limits, suspension systems, highway infrastructure, urban sprawl, and emissions. Each solution was, in essence, a problem in disguise, waiting for its turn to emerge.
The digital world is no different. The transition from local servers to the cloud solved the problem of hardware maintenance — no more physical machines to rack, cool, or replace. But it created the problems of unpredictable cost scaling, infinitely complex IAM permissions, and the slow pressure to redesign every system as “cloud-native.” The relief was real. So were the new burdens.
This pattern shows up everywhere once you notice it, and the field of engineering is no exception. The day this clicked for me, it radically transformed how I built systems. I realised this is not a bug but a feature of the universe — and it completely changed my understanding of what a problem actually is.
You’ll probably notice this in the company you work for: you are always providing some kind of solution to a user problem. What usually goes under the radar is that the product itself has its own telos — its own inherent purpose or trajectory that may differ from your original intent. Telos is an Aristotelian concept meaning the ultimate purpose or end goal toward which something naturally develops. Once a product hits the market, it begins pulling in its own direction, revealing what it “wants” to become through user behaviour, market forces, and emergent use cases.
The product you launched is rarely the product you ultimately have. It becomes not what the founders wanted, not what the market demands, and definitely not what the roadmap says — but rather emerges from the intersection of all of those. Slack started as an internal communication tool for a gaming company; its telos was to replace email for entire organisations. Twitter was meant to be a status update service; it evolved into a real-time news and public discourse platform.
What It Looks Like in Practice
The engineering examples are everywhere once you know to look:
- Kubernetes solved container orchestration and became the problem of YAML complexity.
- React solved DOM manipulation and became the problem of state management theology.
- TypeScript solved JavaScript’s permissiveness and became the problem of build pipeline complexity.
Deploy a caching layer to solve latency — you’ve migrated the problem to cache invalidation, consistency windows, and warm-up strategies. Introduce microservices to solve organisational scaling — you’ve migrated the problem to service boundaries, distributed tracing, and deployment coordination. Adopt AI-assisted coding to solve velocity — you’ve migrated the problem to review quality, architectural coherence, and the subtle erosion of systems thinking in developers who no longer struggle through implementation details.
Here is what that migration looks like mapped across common engineering decisions:
| Solution | Problem it Solved | New Problem it Created |
|---|---|---|
| Microservices | Organizational bottlenecks & monolith complexity | Distributed system complexity, service boundaries, deployment coordination |
| Caching Layer | Database latency & query performance | Cache invalidation, consistency windows, cold-start performance |
| AI-Assisted Coding | Development velocity & boilerplate reduction | Architectural drift, review burden, erosion of systems thinking |
| Cloud Infrastructure | Hardware procurement & data center maintenance | Infinite cost scaling, IAM complexity, vendor lock-in |
| TypeScript | JavaScript runtime errors & type ambiguity | Build pipeline complexity, type definition maintenance |
Technical Debt: The Purest Expression of This Duality
Technical debt is perhaps the purest expression of this duality in engineering. It’s often spoken of as a problem to be eliminated, a sin to be confessed, a backlog to be cleared. But technical debt is better understood as a solution that has aged into a problem — or more precisely, a solution that solved a problem and is now itself a problem in a changed context.
The monolith that feels like a burden today was the solution that let your team ship fast when you had six engineers and no users. The microservice that feels like over-engineering today was the solution that let two teams work in parallel without stepping on each other.
Technical debt, then, is only a decision to be re-evaluated. This is why “paying down debt” is often the wrong metaphor. You’re not returning to a pure state. You’re making a new trade-off, in new conditions, with new information. The old solution was valid for its time. The new solution will be valid for this time. Both will become problems eventually. That is the nature of the work, and what progress actually looks like.
Engineering Stoicism: Accepting the Infinite Game
Recognising this continuous cycle shifts our mindset from seeking a final, static solution to understanding that we’re always managing a dynamic equilibrium of problems. This awareness is a form of engineering stoicism — accepting that problems never “stay solved” helps teams stay resilient because they stop expecting a finish line that doesn’t exist.
Engineers often feel burnt out because they believe there should be a moment when everything just works. When you accept the problem-solution duality, you stop experiencing each new issue as a failure and start seeing it as the natural consequence of progress. You’re not failing to reach stability — you’re successfully migrating toward more interesting problems. This psychological shift is what separates teams that thrive from teams that churn.
How This Translates Into Daily Practice
First, invest in problem articulation as a first-class engineering activity. Before architecture reviews, before sprint planning, before code review, there should be problem review. What exactly are we solving? What would change if we solved it? What new problems would we welcome, and which would we regret? This sounds like overhead. It’s actually acceleration. A team that solves the wrong problem efficiently is slower than a team that solves the right problem messily.
Recently, someone on my team proposed adding bulk actions to our admin panel. We stopped and asked: what are admins trying to accomplish in bulk? Turns out they were correcting recurring data entry mistakes. The real problem wasn’t “no bulk actions” — it was “too many mistakes happen during entry.” We built better input validation instead. Shipped in a week versus the month bulk actions would have taken.
Second, treat constraints as design partners, not enemies to defeat. The budget limit, the latency requirement, the regulatory boundary, the small team size — these aren’t unfortunate obstacles. They’re the only reason your solution will have shape and character. Remove all constraints and you get bloated, generic, expensive systems that solve everything and matter to no one. The best products often emerge from the tightest constraints because the constraints did the design work.
Third, build problem-migration awareness into your retrospectives. Don’t ask “what went wrong and how do we fix it?” Ask “what problems did we solve, what problems did we create, and are we happy with the exchange?” This shifts retrospective energy from blame and repair to strategic choice and pattern recognition. Over time, teams develop taste — not just for solutions, but for the quality of problems they’re willing to live with.
The New Scarce Resource: Problem-Framing in the AI Era
How we frame a problem fundamentally dictates the solutions we even consider. We’re experiencing a fundamental shift from problem-solving to problem-framing. An AI can generate twenty solutions to a technical challenge in thirty seconds. The scarce resource now is problem-framing.
If our problem statement is flawed, or if our underlying assumptions are narrow, AI will — with impressive efficiency — deliver equally flawed or narrow solutions. The role of a senior product engineer today is less about being the sole problem-solver and more about being the expert problem-definer.
Leveraging AI allows for the exploration of a vast solution space, the analysis of data at unprecedented scales, and rapid prototyping. However, the human element — the ability to ask the right questions, to empathise with the user’s unspoken needs, to anticipate unintended consequences, and to engage in true systems thinking — remains paramount.
Communicating the Duality to Stakeholders
Product managers, executives, and users often want problems eliminated. They want clean narratives of progress. Your job isn’t to disillusion them, but to reframe progress as problem migration with intention. When they say they want solutions, what they actually want is results — and results come from solving the right problems.
Help them understand that shipping a feature isn’t the end of the story — it’s a chapter transition. The question isn’t “did we solve it?” but rather “did we migrate to problems we’re better equipped to handle?” This reframing turns what looks like endless firefighting into what it actually is: strategic problem evolution.
The next time you’re about to ship a “perfect” solution, ask yourself: Am I ready to live with the problems this is about to create?
Leave a response