Wiring Modules Part 1: The Art of Writing Modules (Hardcoded Dependencies)

in #programming8 years ago (edited)

Welcome to part 1 of my series Wiring Modules, which aims to summarize chapter 7 in the book Node.js Design Patterns by Mario Cascario. You can find the intro post with links to the other parts in the series here.

The Art of Writing Modules


Hardcoded Dependencies Definition: Obtained when a client module explicitly loads another module using require(). Be careful when hardcoding dependencies with stateful instances, as this would limit the reusability of our module.

Pros & Cons

Pros

  • Immediately intuitive organization, easy to understand debug, where each module initializes and wires itself without any external intervention.

Cons

  • Hardcoding the dep on a stateful instance limits the possibility of wiring the module against other instances, makes it less reusable and harder to test.

Holy Grail for Module Writing

  1. Modules should have a very specific purpose. Don't aim to build a large framework (YAGNI).
  2. Naming Conventions
    • A base filename should exactly match the name of its default export. (AirBnb 22.6)
    • Use camelCase when you export-default a function. Your filename should be identical to your function's name. (AirBnb 22.7)
    • Use PascalCase when you export a constructor / class / singleton / function library / bare object. (AirBnb 22.8)
    • Use kibob-case for folders and npm package names, generally favour clear and "boring" names for better discoverability and code clarity.
  3. Small Focus: Strip away code that doesn't belong in the module, keeping the entry point focused and narrow. I aim for 150 lines or less per a module.
  4. Discoverability
    • Include a README.md in your module folder
    • Comment code, don't overdo comments it takes too long, be concise
  5. Readability
    • module.exports on the bottom of the page.
    • New line after blocks and single line if statements
    • New line before returns, line comments, and function calls with anonymous function parameters
  6. Avoid Global State: Globals, static fields, and singletons are dangerous in module code, and should be avoided. If two modules both depend on foo-bar, and both of them mutate the global state, only one would succeed.

Examples

  1. Database Page Iterator w/ Bluebird Coroutine
  2. DynamoDB Rate Limiter Middleware

Unit Testing

Really make an effort not to test private functions, see this SO post. But at the same time if theres too much code inside of one method its going to be harder to achieve good unit test coverage. If your exported function is too big or you think its going to be to hard to test your module, then you should break it into smaller testable modules. If you can't think of abstractions to break up your module then add a utils module inside your main module folder, but prefer coming up with a reusable abstraction. Don't resort to conditional exports which export functions only in testing environments, this will make your code dirty.

References

Airbnb Style Guide
Module Best Practices

Learn More

Export Interface Design Pattern