Stop Overengineering Your Background Task Infrastructure
In the world of software engineering, there is a common trap that many teams fall into early in their product lifecycle: over-engineering for scale that hasn't arrived yet. We see it constantly—startups deploying complex Kubernetes clusters to manage a Redis instance just to handle a few hundred email notifications per day.
While distributed systems are powerful, they come with significant operational overhead. If your primary goal is simply ensuring that a task (like processing an image, sending a notification, or updating a database record) gets executed reliably after a web request finishes, you don't always need a massive distributed memory grid. You just need a reliable queue.
This is the core philosophy behind Ezra, a lightweight task queue built on Erlang/OTP and backed by SQLite. It represents a "back-to-basics" approach that prioritizes reliability and simplicity over unnecessary complexity.
The Power of Pragmatism: Why SQLite?
The decision to use SQLite as a backend is the most significant architectural choice in Ezra's design. For many developers, the instinct is to reach for Redis or RabbitMQ immediately. While those are excellent tools, they require careful configuration, memory management, and monitoring.
SQLite, on the other hand, provides "durability by default." When you move from an in-memory system (where a crash can wipe out your pending jobs) to a disk-backed system like SQLite, you gain peace of mind. If the server restarts or the process crashes, the data remains on the disk.
The performance trade-off is often overstated for small to medium workloads. Modern NVMe drives are incredibly fast at sequential and random writes. For most applications—where "fast" means a human doesn't notice a delay in an email arriving or a profile picture being processed—the speed of SQLite is more than sufficient. By choosing SQLite, Ezra allows developers to focus on their business logic rather than managing the intricacies of a distributed cache cluster.
Erlang/OTP and the RESP3 Advantage
One might wonder why Erlang was chosen as the foundation for this project. Erlang (and Elixir) were built specifically for high-concurrency systems and fault tolerance. The Erlang/OTP framework provides a robust environment for managing processes that need to stay alive, handle errors gracefully, and manage state reliably.
However, the real "magic" of Ezra's architecture lies in its communication layer: RESP3.
By implementing the RESP protocol at the wire level, Ezra mimics Redis behavior exactly. This is a masterstroke of pragmatic engineering. Because it speaks the same language as Redis, you don't need to write custom drivers or specialized libraries for every language in your stack. If you have an existing Python, Node.js, Go, or Ruby library that connects to Redis, those libraries will work with Ezra out of the box.
This "plug-and-play" compatibility means that as your team grows and adopts different languages for different microservices, they can all consume from the same queue infrastructure without needing specialized configuration for each language's unique requirements.
When to Choose Simplicity Over Scale
Choosing a tool like Ezra isn't about avoiding scale; it’s about choosing the right tool for the current stage of your product.
Choose an architecture like Ezra if:
- You need persistent storage so jobs aren't lost during deployments or crashes.
- Your traffic is low to medium (e.g., thousands of tasks per day, not millions).
- You want a "set it and forget it" infrastructure that doesn't require constant tuning.
- You have a multi-language environment but don't want to manage multiple different queue types for each language.
Consider moving to a distributed system (like RabbitMQ or high-scale Redis) if:
- You need sub-millisecond latency across a massive, geographically distributed cluster.
- Your throughput requirements exceed the capabilities of your local disk I/O.
- You require complex routing logic that only a dedicated message broker can provide.
For most startups and internal tools, the "simpler" path is often the faster way to reach production-ready status. By removing the complexity of managing a distributed memory grid, you reduce the surface area for bugs and infrastructure failures.
Building Your MVP with Intentionality
When building an MVP (Minimum Viable Product), every architectural decision should be weighed against its impact on your speed to market. Every extra "feature" in your infrastructure is a potential point of failure that you have to debug, monitor, and maintain.
If you are currently struggling to decide between a complex distributed system or a simpler, more focused solution for your background tasks, it helps to look at the trade-offs clearly. Sometimes, the most sophisticated engineering choice is the one that allows you to ship faster with fewer moving parts.
If you're looking to streamline your backend architecture and build a robust MVP without the bloat of over-engineered infrastructure, I can help you navigate these technical decisions. Contact me here to discuss how we can optimize your engineering roadmap for speed and reliability.
Summary of Technical Trade-offs
To summarize the core philosophy: Ezra trades "infinite" horizontal scalability for "sufficient" local performance and extreme simplicity. By leveraging Erlang's stability, SQLite's persistence, and Redis's protocol compatibility, it provides a high-reliability path for developers who want to get their features into production without the overhead of managing complex distributed systems.

