We were building a browser extension to capture emails into our internal system. Standard workflow: user clicks a button, email content gets saved, any images come along for the ride.

The images were the problem. Where do you put them?

The "Proper" Solution

Initial plan: route images through MinIO. It's S3-compatible, self-hosted, and feels like real architecture. Pre-signed URLs for security. Proper separation of concerns. This is how the big systems do it.

So we set it up. MinIO container in Docker. Access policies configured. Pre-signed URL generation working. Upload endpoint ready.

Then came the CORS issues.

Browser extension to MinIO. Different origin. Pre-flight requests. Headers not matching. An hour of debugging later, we had it mostly working. Mostly.

Then came the questions:

Each question spawned more infrastructure. More configuration. More things to maintain.

The Question That Changed Everything

In the middle of writing a health check for the MinIO connection, I stopped and asked:

Do we actually need separate storage?

The images are already in the email. They're already in the DOM when the extension runs. We're literally looking at them.

What if we just... kept them there?

// Instead of uploading to MinIO...
const imageUrl = await uploadToMinIO(imageData);

// Just encode what we already have
const imageData = canvas.toDataURL('image/png');
// Store inline with the email content

Base64 encode the images. Store them inline with the email content. Done.

I deleted the MinIO configuration. Removed the Docker container. Deleted the upload endpoint. Removed the pre-signed URL generator.

The feature works exactly the same. But now there's:

The "architecture" became a few lines of inline encoding.

The Pattern

This happens more than we'd like to admit. The progression goes:

  1. Identify a requirement (store images)
  2. Think about how "real" systems do it (separate object storage)
  3. Build the proper solution (MinIO, S3, whatever)
  4. Deal with the complexity that comes with it
  5. Eventually ask if we needed that solution at all

The mistake is jumping from step 1 to step 2 without asking: what's the simplest thing that could work?

Not the simplest thing that feels like real engineering. The simplest thing that solves the actual problem.

The Test

When adding infrastructure to solve a problem, first ask: can I delete the problem instead? If the images are already in memory, you don't have a storage problem. You have data you're choosing to re-upload for no reason.

When This Applies

This isn't an argument against architecture. Complex systems need proper infrastructure. S3 exists for good reasons. Microservices solve real problems.

But there's a class of problems where the "proper" solution is overkill:

For these, every piece of infrastructure you add is a liability. It needs maintenance. It can fail. It adds complexity to debugging. It makes onboarding harder.

The best architecture for these cases isn't the cleanest design. It's the one with the fewest moving parts.

The Uncomfortable Truth

Engineers like building things. We like seeing systems come together. There's satisfaction in setting up proper infrastructure, configuring services, making things "production-ready."

But sometimes the most valuable thing you can do is delete code. Remove a service. Collapse a layer of abstraction. Turn an external dependency into ten lines of inline logic.

It doesn't feel as impressive. You can't really put "removed three services" on a resume the same way you can put "architected distributed image storage system."

But the system that works with fewer parts is usually the system that keeps working.

The Check

Next time you're about to add infrastructure, pause. Write down what problem it solves. Then ask: is there a way to make this problem not exist? The best dependency is the one you don't have.

The Takeaway

Sometimes the best architecture isn't the cleanest design. It's the one you can delete.

When you find yourself adding infrastructure to solve a problem, first ask: can I delete the problem instead?

If the answer is yes, you've just saved yourself hours of maintenance, debugging, and documentation. And you've made the system simpler for everyone who comes after you.

That's not lazy engineering. That's good engineering.