DEV Community

Cover image for The Moment Your Tool Supports 100 Files, It Becomes a Different Product
Muhaymin Bin Mehmood
Muhaymin Bin Mehmood

Posted on

The Moment Your Tool Supports 100 Files, It Becomes a Different Product

A tool that processes one file and a tool that processes 100 files may share the same conversion function.

They are still different products.

The moment users can drop a large batch into your app, almost every surrounding decision changes.

One-file logic

A one-file flow can be brutally simple:

const result = await processFile(file);
download(result);
Enter fullscreen mode Exit fullscreen mode

The UI can show a spinner.

If the operation fails, show an error.

Done.

Now add 100 files

The naive implementation becomes:

const results = await Promise.all(
  files.map(processFile)
);
Enter fullscreen mode Exit fullscreen mode

This looks elegant.

It can also be a terrible idea.

If every file is a large image, you may suddenly decode dozens or hundreds of high-resolution bitmaps simultaneously.

Memory spikes.

The tab freezes.

Mobile devices suffer.

The browser may kill the page.

Bulk support requires resource management.

Controlled concurrency

A better design uses a limited number of active jobs.

Conceptually:

Queue: 100 files

Active:
[1] processing
[2] processing
[3] processing
[4] processing

When [2] finishes:
take next item from queue
Enter fullscreen mode Exit fullscreen mode

This gives you a lever.

You can adjust throughput without creating unbounded pressure.

The correct limit depends on:

  • average file size
  • image dimensions
  • CPU cost
  • memory usage
  • target device class

The important principle is simple:

Maximum parallelism is not the same thing as maximum useful performance.

Progress becomes a first-class feature

With one file, a spinner is enough.

With 100 files, the user wants answers:

  • How many are complete?
  • Which file is processing?
  • Did any fail?
  • Can I cancel?
  • Can I download completed outputs?
  • Is the app stuck?

A useful progress model might show:

63 / 100 complete
4 processing
31 waiting
2 failed
Enter fullscreen mode Exit fullscreen mode

That is much more informative than an indeterminate loader.

Errors should not kill the batch

Another difference:

If 1 file fails out of 100, should the entire job fail?

Usually not.

Bulk operations need per-item error isolation.

A better result can look like:

✓ photo-001.jpg
✓ photo-002.jpg
✗ broken-file.tiff
✓ photo-004.jpg
Enter fullscreen mode Exit fullscreen mode

Then the user can retry or inspect only the failed items.

This is a general batch-processing principle:

One bad record should not destroy 99 good results unless atomicity is actually required.

Download UX changes too

Downloading 100 files one by one is technically possible.

It is also horrible UX.

Users expect one packaged output.

That is why ZIP generation naturally appears in bulk tools.

The workflow becomes:

process items
   ↓
collect outputs
   ↓
package
   ↓
download once
Enter fullscreen mode Exit fullscreen mode

The packaging step becomes part of the product.

Naming becomes important

Single-file tools can get away with generic output names.

Bulk tools cannot.

If the user uploads:

product-01.jpg
product-02.jpg
product-03.jpg
Enter fullscreen mode Exit fullscreen mode

the outputs should remain recognizable:

product-01.webp
product-02.webp
product-03.webp
Enter fullscreen mode Exit fullscreen mode

Otherwise the user saves time during conversion and loses it again during file cleanup.

Cancellation matters

A 500-file job may take long enough that the user changes their mind.

A production-quality system should think about cancellation.

In browser code, that may involve:

  • abort signals
  • worker termination
  • queue state
  • cleanup of temporary objects

The UX should clearly distinguish:

  • queued
  • active
  • completed
  • cancelled
  • failed

Memory cleanup is part of correctness

Browser image processing often creates:

  • ImageBitmap
  • canvas buffers
  • object URLs
  • blobs

If you keep unnecessary references alive, memory usage can climb during a large batch.

Bulk testing should include long runs, not just successful 5-file demos.

This problem shaped BatchSet

One of the reasons I built BatchSet's Bulk Image Converter was that I wanted the workflow to focus on the batch as a unit.

Not:

"Here's a converter that technically accepts multiple files."

But:

"How should the app behave when the batch itself is the user's job?"

That framing changes the product.

A checklist for bulk tools

If you are adding multi-file support, I would review:

  • [ ] controlled concurrency
  • [ ] per-item status
  • [ ] total progress
  • [ ] failure isolation
  • [ ] retry behavior
  • [ ] cancellation
  • [ ] predictable filenames
  • [ ] memory cleanup
  • [ ] one-click packaged download
  • [ ] mobile / low-memory testing

Final thought

"Supports multiple files" is not a checkbox.

It is an architecture decision.

The processing function may be the same.

Everything around it changes.

And that is why the moment your app supports 100 files, it becomes a different product.

Top comments (0)