Handling Ambiguity in the Age of Agents

Ambiguity is not simply having a difficult problem. It is not knowing exactly what the problem is, what matters most, or which direction will create value.

It appears during requirements, scoping, analysis, and design—across the whole solution space. It also appears across teams: product, design, engineering, and the business. Ambiguity has always been one of the hardest parts of engineering. Agentic AI makes it worse at scale.

A human engineer receiving an ambiguous request may push back, ask questions, make assumptions, or stall. An agent immediately produces something: code, commits, documentation, and a confident summary. The output looks like progress. It may have little to do with the outcome anyone wanted.  We always had this problem, but now agents are a huge employer.

Agents lower the cost of building before the cost of misunderstanding has been paid. That changes how we need to handle ambiguity—not less carefully, but more. Let’s take a request that sounds clear and follow it all the way through:

Modernize the platform.

What is hiding inside the request?

“Modernize the platform” hides many questions.

What is actually failing? Who is affected? Are we trying to improve reliability, speed, usability, developer experience, or cost? How much modernization is enough? What should change first?

The work begins by making those questions visible. Handling ambiguity means taking something big and messy and digesting it into smaller chunks. At every step, you make decisions that simplify the problem, narrow the scope, and reveal where the value may be.

The objective is not to predict the perfect answer from the beginning. It is to create a process through which the answer becomes clearer. It's anti-fragility; it does not prevent failure, but it recovers from it.

Is this one problem or five?

Sometimes the difficulty is not technical. The request simply contains too many possible directions. Suppose the team investigates “modernize the platform” and discovers that it actually refers to three different complaints:

  • Deployments are slow and risky (CI/CD is still a big problem).
  • One legacy service fails every week (usually a distributed monolith).
  • The frontend framework is three major versions behind (Let's not talk about how many versions the backend is behind).

That is not one project. It is three projects with different owners, risks, and potential payoffs. Before building anything, the most useful questions are:

  • What is the smallest useful version?
  • Which piece would teach us the most?
  • What is explicitly outside the scope?

Reducing scope is not only about making the project smaller. It makes the learning clearer. Using agents with a high-paced output (which is the default nowadays) does not guarantee learning. Our brains do not process information faster just because agents are 10x faster than we are. When many changes land together, it becomes difficult to know why the result succeeded or failed. A smaller scope creates fewer variables and a cleaner feedback loop. The trend is to do more, which means having more waste and even more ambiguity, because doing more does not mean you have more clarity. 

The first piece should not always be the easiest one. It may be the one that tests the riskiest assumption, creates the most value, or reduces the most uncertainty. In this case, the legacy service (monolith) that fails weekly is the best place to start. It is hurting users now, and working on it will reveal how deep the platform’s problems actually go.

Where is the value?

Value can mean solving a real user problem, reducing risk, saving time, lowering cost, improving reliability, or making something easier to use. The important thing is to know whether anybody cares before investing heavily in polish.

Three questions do most of the work:

  • What pain, friction, cost, or risk are we reducing—and how often does it happen?
  • What evidence suggests anybody cares?
  • Are we solving a real problem or polishing an interesting idea?

The same discipline applies when looking at competitors.

A competing platform may use a newer framework, but that does not mean our users receive value from the same investment. Some competitor patterns become user expectations and should feel familiar. Differentiation should happen only where it creates value that users can notice and explain.

The goal is not to be different everywhere.

For our modernization effort, the value question changes the plan. Nobody outside engineering cares that the frontend framework is old. Support, sales, and users all care that the legacy service fails weekly. The value is in reliability. The framework upgrade can wait (IF you are not regulated). Find the value first. Polish it second.

It's easy to ignore quality; it's easy to build technical best. What people often forget is that this is a corporate boomer gang because if you can't reason about the systems, you will have more ambiguity. Value is not always visible.

Research as a way to reduce uncertainty

Most engineering work does not begin from scratch. You are usually entering an existing system with history, assumptions, workarounds, dependencies, and decisions that are no longer obvious. Reading the code is research.

So is tracing a user action through the UI, services, and database. So is reading tests to discover expected behavior, reviewing incidents and pull requests, talking to users, examining logs, and building small proofs of concept.

This is where an agent can genuinely help. An agent can inspect a codebase (IF it fits in the context window), trace dependencies, compare approaches, summarize previous decisions, and identify unanswered questions across multiple sources—not only in response to a single prompt.

For the failing legacy monolith, an agent could produce an initial map (inventory) of:

  • Its APIs and consumers.
  • Its database interactions.
  • Its external dependencies.
  • Its deprecated libraries.
  • Gaps between documentation and implementation.
  • Paths with weak or missing test coverage.

That map does not replace the engineer’s understanding. It provides a faster first pass.

In the right environment, an agent can compress days or weeks of initial archaeology into a much shorter investigation. But its conclusions remain evidence to be inspected, not truth to be accepted. Agents can misunderstand code, rely on weak sources, and confidently connect unrelated things. Their findings should be traceable and checkable. Research can also become endless.

The purpose is not to answer every possible question before moving. It is to learn enough to make the next decision responsibly.

How deep does this need to go?

A feature—or a fix—can exist at many levels of completeness:

  • Proof of concept.
  • Prototype.
  • Experiment.
  • Beta.
  • Production capability.

Before implementing anything, the team should agree on the current learning goal and define what “good enough” means for this iteration. What would be dangerously underbuilt? What would be overengineering right now? Which user journeys must work? Which edge cases matter today, and which can wait?

Without that discussion, teams often build production-level solutions for assumptions that have not yet been validated. How are people intended to have these discussions if everybody is busy tokenmaxxing and just handling agents 24/7? 

Agents make this risk larger because they can produce code quickly. When implementation becomes cheap, the temptation is to build more before understanding what should exist. A team that once spent a week debating whether to build something may now spend an afternoon building it—and a month living with it.

More output is not more progress.

For our legacy system, the team deliberately chooses a shallow first iteration:

  • Capture the existing behavior in tests.
  • Add observability.
  • Change nothing else.

Boring on purpose. The goal is to establish ground truth before replacing anything.

Discovery and delivery

Sometimes you are in discovery mode: still figuring out the problem, the users, the value, and the direction. Discover it's not a waterfall; it does not die because you went from 0-1. I would argue that some features are products in disguise (don't we need to discover again?). 

At other times, you are in delivery mode: the direction is clearer, but you are still learning while you build.

Discovery reduces uncertainty about what should be built. Delivery reduces uncertainty about how to build, operate, and improve it well. Agents can support both.

During discovery, they can gather evidence, compare approaches, analyze feedback, challenge assumptions, and create prototypes. But keep in mind agents are not customers (I mean, in some cases, they are). During delivery, they can inspect code, generate tests, identify risky dependencies, review changes, investigate CI failures, and perform bounded refactoring.

But agents should not silently decide what matters. They can investigate options and execute bounded work. Humans still own the definition of value, acceptable risk, priority, architecture trade-offs, and the decision to deploy.

Those are not implementation details. They are accountable.

Keeping decisions easy to change

Software makes ambiguity especially difficult because one decision can quietly create a large amount of future work. An early architectural choice, database structure, or interface becomes expensive to change once users, data, and other systems depend on it.

When the direction is uncertain, prefer moves that remain reversible.

The useful questions are:

  • Which decisions become expensive after users or data depend on them?
  • Can the feature be disabled?
  • Can the migration happen incrementally?
  • Can the change be rolled back?
  • Are we preserving flexibility around a real uncertainty—or adding abstractions for imaginary futures?

One way to preserve changeability is through contracts: stable agreements between parts of a system, such as APIs, interfaces, schemas, and events. When boundaries are well designed, the implementation can evolve without breaking everything upstream or downstream.

Agents can inspect whether an implementation follows a contract, generate contract tests, identify breaking changes, compare schemas, and find affected consumers. But contracts designed too early or around the wrong assumptions become constraints.

The goal is not maximum flexibility. Maximum flexibility often becomes maximum complexity. The goal is to preserve options where uncertainty is real and where changing direction later would be expensive. For our service, that leads to one concrete decision:

The replacement must honor the existing API contract exactly, verified by contract tests, so consumers do not need to know that the internals changed. Inside that boundary, everything is fair game.

Agents have the power to increase the API/Contrat blast radius to levels not even thought of before. Tight control of the contracts is a good idea; unlishing the agent on the internal implementation allows reversibility, or what Amazon calls two-way doors.

Unknown unknowns

Some unknowns are known: questions you already know you need to answer. Others are unknown unknowns: assumptions, dependencies, and risks you have not considered yet. Some problems only appear in production. Real users, real traffic, real data, and real integrations expose things that test environments cannot completely reproduce.

But many risks can be surfaced earlier through:

  • Prototypes.
  • Small releases.
  • Feature flags.
  • Observability.
  • Failure testing.
  • Production-like environments.
  • Rollback exercises.

Agents can help search for hidden risks. They can inspect logs, correlate failures, generate unusual test scenarios, and challenge a proposal:

  • What must be true for this to succeed?
  • Which assumptions are being treated as facts?
  • Which dependencies have not been considered?
  • Which failures would be expensive or irreversible?

But agents also introduce new unknowns. They may misunderstand the code, invent an explanation, or optimize for the assigned task while missing the wider context.

Useful agentic systems therefore make their work visible:

  • Which sources were used?
  • What changed?
  • Which assumptions were made?
  • Where is the agent uncertain?
  • Which actions were taken?

Their recommendations should be testable, reviewable, and reversible—the same standard we should apply to any risky change.

From an ambiguous request to an agentic loop

This is where ambiguity connects to Loop Engineering: structuring agent work as bounded, verifiable, repeatable loops instead of open-ended prompts.

“Modernize the platform” is not an agentic task.

Neither is:

  • Improve the user experience.
  • Make the system scalable.
  • Fix the technical debt.
  • Refactor the codebase.
  • Make it enterprise-grade.

An agent will still produce something from these instructions.

That is the danger.

Everything we have done so far—exposing the hidden questions, reducing scope, locating value, researching the system, choosing the right depth, and protecting the contract—has been converting an ambiguous intention into bounded work.

After that process, the modernization request may look like this:

  • Identify deprecated dependencies in the legacy billing service.
  • Map which components consume them.
  • Add tests around the existing behavior.
  • Produce a migration proposal for human review.
  • Upgrade one dependency.
  • Run the defined checks.
  • Stop before deployment.
  • Request review.

Now it is an agentic task.

Notice what it has that the original request did not:

  • A clear goal.
  • A bounded surface.
  • Defined verification.
  • Stopping conditions.
  • Human escalation.

A useful loop also needs permissions, isolation, observability, a budget, and a rollback path.

Before handing work to a loop, ask:

  • Is the task bounded?
  • Can success be verified?
  • Is the result easy to review?
  • Is the change easy to roll back?
  • When should the agent stop?
  • When should it escalate?
  • Who owns the result afterward?

The difference between an ambiguous request and a useful agentic task is not a better prompt.

It is a better-defined system of work.

What does done mean?

The team also needs to define done. What observable behavior must change? What must not change? Which metrics should improve? Who verifies the result? Can the change be operated, explained, and maintained safely? Verification may include tests, but it is not limited to tests.

It may also include:

  • Operational metrics.
  • User behavior.
  • Contract compatibility.
  • Security constraints.
  • Performance thresholds.
  • Human review.

“Done” cannot mean only that an agent produced code or that the tests turned green.

A loop optimizes for its verifier. If the verifier is weak, the agent may satisfy the check without solving the real problem.

It might weaken a test, avoid an edge case, or produce an implementation that technically passes while missing the intended outcome.

For our billing service, done means:

  • The weekly failure no longer appears in the incident log for one month.
  • The contract tests pass.
  • No consumer needs to change its code.
  • An engineer who did not write the change can explain and operate it.

Output is not outcome.

What changes on Monday morning?

Lean and agile have already taught us the shape of this work. Lean says: Do not invest heavily before value is clear. Agile says: learn and adjust as you deliver. Both encourage smaller bets, shorter feedback loops, continuous learning, and decisions that keep options open.

Agentic AI can strengthen those loops. It lowers the cost of research, speeds up experiments, and shortens the distance between a question and the evidence needed to answer it. But it also removes a safety mechanism we may not have realized we had: the cost of building used to force us to think first.

Now thinking is a discipline we have to choose.

The useful question is no longer:

How much can the agents produce?

It is:

How much uncertainty can the agent help us remove before we make the next investment?

The path through ambiguity is straightforward, even when the problem is not:

  • Expose the hidden questions.
  • Reduce the scope.
  • Locate the value.
  • Research the system.
  • Bound the work.
  • Execute carefully.
  • Verify the outcome.
  • Learn from what happens next.

Reduce the size of the bet. Shorten the feedback loop. Preserve the ability to change.

Use agents to accelerate the learning—not to avoid the thinking.

Find the value first. Polish it second.

Popular posts from this blog

Harness Engineering

Cool Retro Terminal

AI coding Agents Evolution