Node.js

Lesson 05

Module side effects and startup code

Avoid starting timers, connections, or jobs when a module is merely imported.

Good Code

src/jobs/cleanup.ts
type CleanupJob = {
  stop: () => void;
};

export function startCleanupJob(runCleanup: () => Promise<void>): CleanupJob {
  const interval = setInterval(() => {
    runCleanup().catch((error) => {
      console.error({ error }, "Cleanup job failed");
    });
  }, 60_000);

  return {
    stop: () => clearInterval(interval),
  };
}

Bad Code

src/jobs/cleanup.ts
import { deleteExpiredSessions } from "../sessions";

setInterval(() => {
  deleteExpiredSessions();
}, 60_000);

export function cleanupNow() {
  return deleteExpiredSessions();
}

Review Notes

What to review

Good Code

The good version makes startup explicit and returns a handle that can stop the background job.

Bad Code

The bad version starts a timer at import time, so tests or scripts that only need cleanupNow also start an ongoing background loop.

Takeaways

  • Side effects should live behind explicit start functions or entry points.