Background jobs should stay boring
A practical note on keeping BullMQ integration thin while still giving apps the queue glue they need.
Background jobs get easier to operate when the application keeps its side of the contract small.
BullMQ already owns the hard parts: queue storage, retries, scheduling, workers, and flows. The app only needs to decide what queue it uses, how a job is named, how data is shaped, and which processor should run for that name.
The thin wrapper
The default jobs package keeps those decisions close together:
queue.tsexports the queue name and queue instance.worker.tsstarts a process that can run in Coolify or locally.examples.tscontains the removable enqueue helper.
That is enough structure for a new app to ship a manual job without turning the boilerplate into a second queue framework.
What to add later
Cron jobs and flows are best added when a real product workflow needs them. At that point, keep the BullMQ API visible. Use upsertJobScheduler for recurring work and FlowProducer for parent-child jobs.
The important part is not avoiding abstraction entirely. The important part is making the abstraction cheap to delete.