The obvious objection
Dependency injection is usually justified by team-scale problems: swapping implementations, isolating modules between developers, enforcing contracts. I'm one person. So why bother?
What actually changed
The honest answer is testing. Before Awilix, my service functions imported their dependencies directly:
// before: hard-wired dependency
import { db } from "../db";
export async function createLead(data) {
return db.leads.insert(data);
}
Testing this means mocking module imports — brittle and framework-specific. With the container, dependencies arrive as arguments:
// after: injected dependency
export function makeLeadService({ db }) {
return {
createLead: (data) => db.leads.insert(data),
};
}
Now a unit test passes a fake db object. No mocking library gymnastics.
What it cost
There is a real tax: indirection. Jumping to a definition sometimes lands in the container registration instead of the implementation. For a solo project, that tax is only worth paying on the layers you actually test hard — for me, that was the service layer, not the route handlers.
Write this section properly with your real reasoning — this file is a placeholder showing the format: frontmatter, prose, code blocks, quotes.