Contents
In brief
Cross-platform Node.js CLIs often devolve into if (process.platform === …) mazes. A Dev.to guide uses Strategy and Factory to build a port-cleanup utility for EADDRINUSE: OS-specific commands hide behind interfaces; core logic stays testable.
What happened
Classic pain: dev server fails because the port is busy. Windows wants netstat/taskkill; macOS/Linux want lsof/kill. Nested platform branches break Single Responsibility and make unit tests painful.
The layered design:
- TypeScript interfaces define “find process on port / terminate” without shell strings in business code.
- A Factory picks the platform Strategy.
- A stateless command runner avoids string-concatenated shells — less shell injection risk.
- Shared API and CLI on one core.
The example domain is “free a port before dev,” but the pattern applies to any environmental variance: binaries, network tools, error handling.
Why it matters
npm tools that “work on my Mac” often fail on Linux CI or Windows teammates. Strategy objects make behavior predictable and mockable — swap the strategy in tests instead of parsing real lsof output.
Same split helps any adapter to external systems (cloud APIs, CI, local Docker): policy vs mechanism.
In practice
- Define the domain interface before the first shell command.
- One Factory at the platform decision point — do not scatter
platformchecks everywhere. - Runner takes argv arrays, not
exec(`kill ${pid}`)— especially ifpidever comes from user input. - Tests: fake Strategy recording calls; integration only on target OS in a CI matrix.
- CLI (commander/yargs) wraps the same service you export from
"exports".
Takeaway
Factory + Strategy in Node.js keeps cross-platform npm packages readable. Read the Dev.to post if your utilities are drowning in switch (os.platform()).

