| name | refactor-module |
| description | Safely refactor a NestJS module or Next.js component — extract abstractions, reduce duplication, improve naming, fix convention deviations. Follows the monorepo's patterns and validates the build after changes. |
| license | MIT |
| compatibility | NestJS + Next.js monorepo |
| metadata | {"author":"project","version":"1.0"} |
Refactor a module, service, or component in this NestJS + Next.js monorepo. Improve the code without changing behavior.
Input: The argument after /refactor-module names what to refactor. Examples:
- A module:
apps/api/src/users
- A service:
apps/auth/src/local-auth.service.ts
- A component:
apps/web/src/components/user-table
- A package:
packages/shared/src/utils
Refactoring Principles
- No behavior changes — refactoring must not change what the code does
- Follow conventions — after refactoring, code must comply with
CLAUDE.md and CONVENTIONS.md
- Build must pass — run
pnpm build and pnpm check-types after changes
- Tests must pass — run tests for affected packages after changes
Step 1: Understand the Current State
Read the target files:
find <target-path> -name "*.ts" -o -name "*.tsx" | sort
cat <target-file>
Identify:
- What does this module/service/component do?
- What does it depend on (imports)?
- What depends on it (who imports it)?
- Does it follow monorepo conventions?
Convention Violations to Look For
NestJS:
process.env outside main.ts → use ConfigService.getOrThrow()
- Hardcoded strings for queue names, event patterns → use
@repo/shared constants
PrismaClient injected directly → use DatabaseService
- Custom JWT/Passport logic → remove, use
better-auth
- BullMQ imports → replace with Bull v4
ZodValidationPipe registered per-module → remove (it's global)
- Missing
SharedModule.register() as first import in AppModule
- Zod v3 APIs (
.string().email()) → update to v4 (.email())
Next.js:
process.env for secrets in client code → move server-side
getServerSession() in client components → move to server component
- Large client components that could be server components
General:
- Files over 500 lines → split
- Duplicate code that could use existing abstractions (
BasePublisher, BaseProducer)
console.log → this.logger.log() or this.logger.debug()
Step 2: Plan the Refactor
Before writing any code, outline:
Planned changes:
1. [description] — [file] — reason: [why]
2. ...
Files affected:
- [file path] — [change type: extract/rename/move/simplify]
Downstream impact:
- [files that import the changed code]
Get confirmation if the scope is large (> 5 files).
Step 3: Execute
Make changes incrementally:
- Rename identifiers (fix all references)
- Move code (update imports)
- Extract abstractions (create new files, update callers)
- Remove duplication (consolidate)
When moving code to packages/shared:
- Add export to
packages/shared/src/index.ts
- Update all importers to use
@repo/shared import
Step 4: Validate
After changes:
pnpm check-types
pnpm build
pnpm --filter <app-or-package> test
Fix any errors before declaring the refactor complete.
Common Refactoring Patterns
Extract to BasePublisher
@Injectable()
class NotificationsService {
constructor(@Inject(SERVICES.NOTIFICATIONS) private client: ClientProxy) {}
async publishEvent(pattern: string, data: unknown) {
this.client.emit(pattern, data);
}
}
@Injectable()
class NotificationsPublisher extends BasePublisher {
publishUserCreated(user: User) {
return this.emit(EVENT_PATTERNS.USER_CREATED, { user });
}
}
Extract to BaseProducer
await this.emailQueue.add('send_welcome', data, { attempts: 3, ... });
class EmailProducer extends BaseProducer {
async sendWelcome(data: WelcomeData) {
return this.add(JOB_PATTERNS.SEND_WELCOME_EMAIL, data);
}
}
Fix Hardcoded Strings
@Processor('email-queue')
@Process('job:send_welcome_email')
@Processor(QUEUES.EMAIL)
@Process(JOB_PATTERNS.SEND_WELCOME_EMAIL)
Fix Direct env Access
const host = process.env.REDIS_HOST;
constructor(private readonly config: ConfigService) {}
const host = this.config.getOrThrow<string>('REDIS_HOST');
Guardrails
- Never change behavior — only structure and names
- Never add new features during a refactor
- Keep each change small and verifiable
- Run build + types after every significant change
- If tests don't exist and the code is complex, suggest adding them before refactoring