Keep Async Boundaries Explicit

keep-async-boundaries-explicit

Summary

Hidden async work obscures cancellation, ordering, resource ownership, and failure behavior. Make task spawning, awaiting, retries, and background ownership visible at the boundary where callers need to reason about them.

Problem

Async code hides important boundaries when locks, borrows, cancellation, retries, or I/O are spread across await points without a clear owner. Callers and reviewers need to know what is held, what can be cancelled, and what external work may already have happened.

Preferred Move

Make async boundaries visible. Gather synchronous inputs before await, keep locks and temporary borrows out of awaited regions when practical, and name operations that perform I/O, retry, spawn, or observe cancellation.

Tradeoff

Do not split every awaited line into a helper. Async structure should clarify ownership, cancellation, and side effects; it should not turn a linear workflow into a maze of weak wrappers.

Agent Instruction

When editing async Rust, identify what crosses each `await`: owned data, borrows, locks, retries,
side effects, and cancellation points. Reshape the code when an `await` hides those obligations from
the API or review.

Examples

Bad: a mutable borrow is held while the function performs network I/O.

pub async fn refresh(project: &mut Project, client: &Client) -> Result<(), RefreshError> {
    let config = project.config_mut();
    let remote = client.fetch_config(config.remote_id()).await?;
    config.apply(remote);
    Ok(())
}

Good: the awaited work uses owned inputs, then the mutation resumes after the boundary.

pub async fn refresh(project: &mut Project, client: &Client) -> Result<(), RefreshError> {
    let remote_id = project.config().remote_id().to_owned();
    let remote = client.fetch_config(&remote_id).await?;

    project.config_mut().apply(remote);
    Ok(())
}

References

Source Use Note
Async book adapts Async functions expose suspension points in the workflow.
Rust reference adapts Async function bodies capture arguments into a future.