DEVLOPN Audit

1000 warnings removed by AI agents — what should you review?

Removing Dart warnings with unawaited() does not fix bugs. How to guide AI agents and review async calls, their dependencies, and error handling in a large PR.

1000 unawaited_futures warnings removed in one afternoon by AI agents can look like substantial progress. Yet some fixes can leave runtime behavior unchanged, including existing bugs.

1000 warnings in one afternoon: AI agents stamp “OK” on the lints, unawaited() swallows the error, while the warning was a question

The risk arises when the instruction is simply “fix all warnings.” At each call site, the agent also needs to determine whether subsequent code depends on the result and how a failure will be handled.

What unawaited() changes

The unawaited_futures rule flags, among other cases, async calls that are not awaited inside an async function. Adding unawaited() makes that choice explicit and removes the warning:

// Before: the lint flags a call that is not awaited.
repository.save(order);

// After: the lint no longer flags this call.
unawaited(repository.save(order));

Runtime behavior stays the same. As the Dart documentation explains, unawaited() does not handle errors: if the Future fails without error handling elsewhere, its error remains unhandled. It does not suppress exceptions or error traces.

If subsequent code assumes the order has already been saved, the problem still exists. The warning that prompted someone to examine the call has simply disappeared.

Choose based on context

When subsequent code depends on the save, wait for its result:

await repository.save(order);

The error can then be handled here or propagated to the caller. Adding await enforces the order of operations, but the failure handling still needs to be decided.

An independent operation can continue in the background if its failure is handled. For example, for a log method that returns a Future<void>:

unawaited(analytics.log('order_created').catchError((Object error) {
  reportError(error);
}));

A plain unawaited(cache.warmUp()) does not handle a cache warm-up failure. Even if a missing cache is acceptable, you need to check what happens to the error.

The discarded_futures rule concerns Future-returning calls in synchronous functions. It also requires context: making a function asynchronous may require changes to its callers.

Scope the agents' work

I start with a read-only pass to identify calls that need to be awaited and those that can continue in the background. For each one, the agent must explain dependencies with subsequent code and the intended error handling. If it cannot justify the choice, the call remains open for review.

The review then prioritizes database writes, payments, and operations that determine whether a user receives a confirmation. Sampling the remaining changes can reveal a recurring mechanical fix, but it cannot guarantee the quality of the whole batch.

The warning count helps track progress. To approve the PR, you need to check that each change reflects a decision suited to the code.