Series: Engineering Judgment, Part 4
Publish date: 2026-07-10

Many engineering projects are not defeated by problems the team cannot solve.

They are defeated by solving problems that are real, difficult, and unnecessary.

The wrong problem rarely looks wrong. It is usually valid, technically respectable, and important somewhere. It becomes wrong because solving it now displaces the constraint that actually determines whether the system can move forward.

At the system level, this is a form of priority inversion.

A secondary concern acquires the strongest architecture, the most design attention, and the largest share of engineering capacity, while the primary outcome remains blocked.

The team is working hard. The design is becoming more complete. The project is still moving in the wrong direction.

From an Outcome to an Expanding Problem Set

A project usually begins with a concrete outcome: support a customer workflow, launch a product path, make a system reliable enough for production, or remove a known bottleneck.

Then implementation exposes adjacent problems.

Some are technically interesting. Some reveal genuine limitations. Some seem irresponsible to leave unresolved. Each one can be justified in isolation, so the accepted problem set gradually expands.

The team stops asking:

What must be true for the system to deliver its primary outcome?

It starts asking:

What would a complete solution to this entire class of problems look like?

That change sounds small, but it changes the architecture.

The project is no longer building the smallest reliable system that satisfies the current constraint. It is building a platform for a larger future that has not yet been demonstrated.

Case One: Multi-Tenant Support Becomes a Platform

Consider a common example: multi-tenant support.

The actual requirement may be narrow:

Allow several customers to use the product with reasonable data separation and operational safety.

That requirement needs clear tenant boundaries, authorization, resource isolation, operational visibility, and a controlled deployment model.

It can quickly expand into a much larger discussion:

  • Should every tenant support independent schema evolution?
  • Should configuration be fully dynamic?
  • Should tenants have isolated deployment controls?
  • Should the data model support every possible future vertical?
  • Should the migration framework preserve every historical compatibility path?

Each question is defensible.

Together, they transform “support multiple customers” into “build a universal platform for tenant-specific system evolution.”

These are not two versions of the same problem.

The first requires bounded capability and a reliable operating model. The second requires platform architecture, migration semantics, policy controls, compatibility guarantees, observability across many modes, and permanent ownership of every supported variation.

The first problem can ship.

The second can consume the project.

The deeper issue is not merely scope creep. The system has accepted a new set of long-term obligations before the original requirement proved that those obligations were necessary.

Case Two: Exactly-Once Delivery Is Not the Same as Exactly-Once Effect

The same pattern appears in distributed transaction and event-processing systems.

Suppose the real requirement is:

A logical payment operation must not charge the customer twice.

A team can easily restate that requirement as:

The entire system must provide exactly-once message delivery.

The second statement sounds cleaner and more rigorous. It is also a much stronger problem.

The system may include a message broker, a database, retry workers, a payment provider, timeout recovery, and manual reconciliation. These components do not naturally share one atomic commit boundary. Attempting to impose end-to-end exactly-once delivery can introduce distributed coordination, durable deduplication state, leases, recovery protocols, failure-sensitive ownership, and new ambiguity around what “once” means after partial completion.

But the business invariant was never “every message is delivered exactly once.”

The invariant was “one logical operation produces at most one external effect.”

A more bounded design may use at-least-once delivery, a stable operation identifier, idempotent state transitions, deduplication at the side-effect boundary, and reconciliation for uncertain outcomes.

This does not solve the more general delivery problem.

It solves the actual correctness problem.

That distinction is central to engineering judgment. A stronger technical guarantee is not automatically a better system if it is stronger than the outcome requires and its coordination cost weakens reliability elsewhere.

The same mistake appears in execution systems. A team that needs higher throughput may jump directly to a universal speculative scheduler, even when the current workload can be partitioned through declared access sets or ownership boundaries. The universal scheduler is a legitimate technical problem, but it also introduces conflict detection, rollback semantics, deterministic replay, tracing complexity, and recovery states. The relevant question is not whether general parallel scheduling is valuable. It is whether that generality is the present bottleneck.

Architecture Is a Record of Accepted Obligations

A system does not inherit only the solutions a team implements.

It inherits the problems the team agrees to support.

Every execution mode becomes behavior that must remain correct. Every extension point becomes a contract. Every compatibility path becomes history that future changes must preserve. Every configuration dimension becomes a new interaction with runtime state. Every recovery mechanism becomes an operational path that must be tested under failure.

This is why implementation estimates systematically understate the cost of generality.

A mechanism may take two weeks to build and years to carry.

The real cost is not the code that creates it. The real cost is the number of futures the system now promises to support.

From this perspective, architecture is partly the accumulated record of which problems were admitted into the system. A large part of simplification is therefore not code cleanup. It is withdrawing obligations that should never have become permanent.

The Wrong Problem Usually Looks Responsible

Engineering drift is difficult to stop because it often presents itself as rigor.

Generality sounds future-proof. Completeness sounds professional. Supporting every edge case sounds safer than rejecting one. A reviewer who asks for broader coverage appears more responsible than the person who argues for a narrower system.

Three forms appear repeatedly:

  • A non-blocking problem: leaving it unsolved does not prevent the core outcome.
  • A premature problem: it may matter later, but the system has not yet learned enough to solve it correctly.
  • An over-generalized problem: a specific requirement has been expanded into a reusable platform capability without evidence that the platform is needed.

All three may deserve attention eventually.

The failure is allowing them to displace the constraint that matters now.

There is also an organizational reason this happens: teams often prefer the problem they can specify over the problem that actually constrains the outcome.

A technical generalization can be decomposed, estimated, assigned, and reviewed. The real bottleneck may depend on production feedback, user behavior, operational learning, or an uncomfortable product decision. Solving the internal technical problem creates visible activity and local certainty. Testing the external assumption may reveal that the architecture was pointed at the wrong target.

Clever technical work can therefore become a form of avoidance.

The team remains busy inside a problem it understands instead of exposing the system to the reality it still needs to learn.

Difficulty Is Not Value

Engineers are naturally attracted to difficult problems. That instinct is useful; many important systems would not exist without it.

But difficulty is not evidence of value.

A problem deserves immediate attention when leaving it unsolved does at least one of three things:

  • blocks the system’s primary outcome
  • creates unacceptable correctness, safety, availability, or operational risk
  • closes a future option that would be prohibitively expensive to recover

Everything else needs a stronger justification.

A hard problem can be technically central and strategically peripheral. It can produce elegant abstractions, deep discussions, and impressive code while changing nothing about whether the system can deliver.

The uncomfortable question is not:

Can we solve this?

It is:

What changes if we do not solve it now?

If the answer is only that the system remains less elegant, less general, or less complete, that is different from the system being unable to work.

Engineering discipline begins with preserving that distinction.

Designing the Boundary of Not Solving

Not solving a problem is not the same as ignoring it.

Unbounded deferral becomes hidden debt. A deliberate bypass path is different: it acknowledges the limitation, contains its consequences, and prevents a secondary concern from capturing the primary architecture.

A well-designed bypass makes several things explicit:

  • what behavior is supported now
  • what behavior is intentionally unsupported
  • which validation or guardrails enforce the boundary
  • how failure is contained when the boundary is reached
  • what evidence would cause the decision to be revisited
  • who owns the limitation and its eventual removal

This may mean constraining inputs, isolating versions, rejecting unsupported workflows, accepting manual recovery for genuinely rare cases, or placing a capability behind a narrow operational process.

The goal is not to pretend the problem does not exist.

The goal is to prevent a non-core problem from becoming the architecture of the entire system.

Deferral without a boundary is debt.

Deferral with a boundary is sequencing.

The quality of the boundary matters. A vague statement that “we will handle it later” is not a design. A clear unsupported state, an explicit rejection path, an observable limit, and a known escalation procedure are design.

A mature system is not one that supports everything.

It is one that knows precisely what it supports and fails predictably outside that boundary.

Protect the Main Path, Not Only the Happy Path

“Main path first” does not mean “ignore failures.”

For infrastructure systems, the main path is not merely the happy path. It is the minimum set of behaviors the system must perform to be useful and trustworthy:

  • execute the ordinary workload correctly
  • preserve critical state invariants
  • contain failure within a bounded blast radius
  • expose enough information to diagnose the failure
  • recover through a known procedure

A system that performs those behaviors with a limited feature set is more mature than a feature-complete system whose failure and recovery semantics are unclear.

Before that foundation is stable, completeness is mostly a liability. The system has not yet earned the additional states, modes, and promises that broader support would create.

Good engineering often means allowing a system to operate with known limitations:

  • support fewer modes
  • constrain inputs
  • defer generality
  • reject unsafe workflows
  • use manual intervention for rare but observable cases
  • preserve a path for later correction

This is not lowering standards.

It is sequencing standards around the system’s actual reason to exist.

A Practical Decision Test

When a technical issue begins absorbing disproportionate attention, stop discussing solutions and classify the problem first.

Ask:

  1. Which concrete outcome does solving this unlock?
  2. What fails if it remains unsolved for the next release?
  3. Does it threaten a critical invariant, or only reduce elegance and completeness?
  4. Can a constraint, isolation boundary, fallback, or manual process contain the consequence?
  5. Would delay create irreversible lock-in or make a future correction prohibitively expensive?
  6. What permanent states, modes, contracts, ownership boundaries, and operational paths would the solution add?
  7. What evidence would prove that the general solution is actually needed?

The action should follow the classification.

If the problem blocks the primary outcome, threatens a critical invariant, or creates unacceptable risk, solve it now.

If it can be bounded safely and revisited after the system learns more, design the boundary and defer it.

If its primary benefit is architectural completeness, hypothetical reuse, or future-proofing without evidence, reject it for now.

This process does not eliminate judgment.

It prevents solution enthusiasm from silently making the priority decision.

The Cost of Solving Too Much

Solving unnecessary problems does more than delay a schedule.

It delays contact with reality. It adds behavior before the team has evidence that the behavior is needed. It creates abstractions around hypothetical use cases. It expands testing and operational obligations. It assigns ownership to mechanisms whose value has not been demonstrated.

Most dangerously, it changes the team’s definition of progress.

The project begins to feel successful when the architecture becomes more complete, even while the original outcome remains unavailable. Technical activity increases, but the system does not move closer to being useful.

That is how projects become architecturally impressive and practically late.

It is also how systems become difficult to simplify. Once a capability has users, data, operators, dashboards, compatibility expectations, and organizational ownership, removing it is no longer a refactor. It is a renegotiation of the system’s obligations.

The cheapest problem to remove is the one the architecture never agreed to solve.

Engineering Judgment Is Problem Selection

Technical competence answers whether a problem can be solved.

Engineering judgment decides whether it is the problem that should be solved now.

A strong team does not ignore difficult questions. It places them in the correct order. It knows which limitations must be removed, which risks must be contained, and which ambitions must wait for evidence.

The best architecture is not the one that anticipates every possible future.

It is the one that solves the current constraint without unnecessarily closing the next set of options.

Some problems need to remain unsolved long enough for the system to ship, meet reality, and reveal what its real constraints are.

A system moves forward not when every problem is solved, but when the right problem is solved without allowing the rest to take control.