esteban@devtrillo:~/blog/writing$
← cd -
$ cat learning-effect-10-concurrency-without-guessing.md

Part 10: Concurrency Without Guessing

Jul 19, 2026·1 min read
$ tail -f learning-effect-10-concurrency-without-guessing/readers
connecting…

At some point, a program needs two things that do not depend on each other.

Fetch the user.

Fetch the invoices.

Those can run at the same time.

Run both

const program = Effect.all({
user: fetchUser(userId),
invoices: fetchInvoices(userId),
});

This gives one Effect with both results.

Effect.Effect<
{ user: User; invoices: Invoice[] },
UserNotFound | InvoiceApiDown,
UserRepo | InvoiceClient
>;

That type is doing useful work.

It shows the combined success, combined errors, and combined requirements.

What is the success value of Effect.all with user and invoices?

Parallel when it is safe

Some work must be sequential.

You cannot fetch invoices for a user id before you have parsed the user id.

But once you have the id, independent calls can run together.

const program = parseUserId(input).pipe(
Effect.flatMap((userId) =>
Effect.all({
user: fetchUser(userId),
invoices: fetchInvoices(userId),
}),
),
);

Which part must happen first?

The nice part

In normal async code, I often have to inspect the Promise.all shape and then think about errors separately.

Effect keeps the shape together.

Success. Error. Requirements.

The same three slots again.