forwardRef fixes the error, not the design

Two modules in a NestJS app I was working on needed each other. UsersModule needed something from BillingModule, and BillingModule needed something back from UsersModule. Nest refused to boot and threw a dependency resolution error, and the fix that shows up in every search result is the same line: wrap the import in forwardRef(). I did that. The app booted. I moved on, which was the mistake.
What forwardRef actually does
@Module({
imports: [forwardRef(() => BillingModule)]
})
export class UsersModule {}forwardRef() does not resolve the circular dependency. It lazily evaluates the reference so Nest can wire up two modules that point at each other, deferring the lookup until both classes actually exist. The official docs are direct about this: circular dependencies "should be avoided where possible," and forwardRef is one of the ways to survive one you could not avoid, not a reason to write more of them.
Check the barrel files first
Before reaching for forwardRef at all, it is worth ruling out the boring cause. Nest's own docs call out barrel files (index.ts re-exports) as a common source of circular dependency errors that have nothing to do with your actual design, linking to a maintainer explanation of exactly this failure mode: cats/cats.controller.ts importing from the cats barrel instead of directly from cats/cats.service.ts can manufacture a cycle that does not exist in your dependency graph, only in how the files import each other. That fix is deleting the barrel import, not adding forwardRef to a dependency that was never really circular.
The dependency was the real bug
Once barrel files are ruled out, a genuine circular dependency between two modules is telling you something: some piece of logic is claimed by both sides. In my case, UsersModule and BillingModule both needed a plan-lookup helper that belonged to neither. Pulling it into its own PlansModule that both modules import removed the cycle outright. No forwardRef, no lazy references, just one fewer wrong assumption about where that logic lived.
A circular dependency is two modules disagreeing about who owns something. forwardRef settles the argument by refusing to pick a side.
When forwardRef is still the right call
Sometimes the coupling is real, and extracting a third module would cost more than the cycle, particularly for two providers that are genuinely part of the same feature and always deploy together. That is what forwardRef, and the ModuleRef class for provider-level cases, actually exist for. The difference is using it once, deliberately, versus reaching for it every time Nest complains, which turns a design signal into a code style you stop noticing.
The next time the dependency graph refuses to boot, the useful question is not "how do I make this error go away" but "why do these two modules think they need each other." forwardRef answers the first question. It has nothing to say about the second.