I strongly advocate splitting large applications into smaller modules. Each module can be understood, coded and tested thoroughly much easier if taken in isolation.
Check dependencies (pull)
Each module lists its dependencies using version numbers. I suggest using strict versions without any fuzzy ranges.
"dependencies": {
"foo": "0.2.1", // good
"bar": "0.*" // bad
}
You can use npm shrinkwrap command to lock down used dependency versions, or grunt-nice-package to enforce strict version strings.
When your module depends on a set of other modules, it will constantly fall behind. The dependencies will release new versions, but your module still depends on old versions. You can add a dependency up to date badge from David service to remind yourself when your package falls behind minor or major releases.
If you trust the published semver numbers, you should be able to use new minor and patch releases right away. Only major releases with API breaking changes are dangerous. Yet in practice, this is never 100% reliable. Some projects do not adhere to semantic versions, incrementing version numbers at will. Other projects do not test enough, breaking your features accidentally during patch releases.
I wrote next-update to make upgrading 3rd party
dependencies an extremely simple operation. Instead of incrementing all versions manually and running
tests to see if any upgrade breaks your project, just run next-update
in your project's folder.
Next-update installs each new release of 3rd party dependency separately, runs your unit tests then
reports all successful and failed dependencies.
// in your project foo
$ next-update
next updates:
lodash
1.2.1 PASS
async
0.2.7 PASS
0.2.8 PASS
bar
1.0.1 FAIL
install updates command: npm install --save [email protected] [email protected]
You can now keep the successful updates and think how to upgrade the libraries that have breaking changes.
Check dependants (before publish)
Let us think about the reverse situation: there are projects that depend on your module. When working on new release, before you actually publish it, wouldn't it be nice if you could check if any dependants would not be able to upgrade right away? There is an open issue on NPM repo about this feature. Most people think it should be a separate tool or service. I wrote CLI tool to do this: dont-break. Here is how works using an example.
Example: checking if new release of lodash would break check-more-types.
step 1 - try current latest version of lodash
Clone lodash, change into its folder and create .dont-break
file. Place published module's name
to be checked
// file .dont-break
check-more-types
You can put multiple project names into the file, each project name at separate line.
step 2 - confirm the current version still work
The current published version of check-more-types depends on lodash@2.4.1 First let us confirm that the current source we are working with (@3.0.0-pre) still works
~/git/lodash on master
$ dont-break
dependents [ 'check-more-types' ]
testing check-more-types
installing check-more-types
installed into /tmp/[email protected]
installing dev dependencies /tmp/[email protected]/lib/node_modules/check-more-types
NPM install in current folder
restoring current directory /Users/gleb/git/lodash
npm test
tests work in /tmp/[email protected]/lib/node_modules/check-more-types
copied /Users/gleb/git/lodash/* to /tmp/[email protected]/lib/node_modules/check-more-types/node_modules/lodash
npm test
tests work in /tmp/[email protected]/lib/node_modules/check-more-types
all dependents tested
PASS: Current version does not break dependents
step 3 - break a function in lodash
lodash is only used in check-more-types to run 1 test. The test uses just a single feature: partial right application
1 | ./test/load-under-node-test.js:12:var _ = require('lodash'); |
let us break _.partialRight
by changing slice to start with third argument.
1 | // dist/lodash.compat.js word diff |
Run dont-break again
~/git/lodash on master*
$ dont-break
dependents [ 'check-more-types' ]
testing check-more-types
...
npm test returned 1
test errors:
AssertionError: false == true
at Console.assert (console.js:102:23)
...
tests did not work in /tmp/[email protected]/lib/node_modules/check-more-types
code 1
FAIL: Current version break dependents
Even if lodash's own unit test passed, we now detect that we would break a dependant.
Conclusions
Your module is part of the NodeJs / NPM environment. It probably depends on modules written by other developers, people you most likely have never met. Your work will also be reused by other people. Errors caused by upgrading 3rd party libraries are usually hard to debug and solve, because they require working across very high boundaries. If we all play nice and plan what we release into the wild at each iteration, applications will break less often, and everyone benefits.
Related posts
Update 1
I added methods to fetch top N most downloaded or starred dependent modules for your module. Thus you do not have to manually pick which modules to test, instead you can support most popular dependants.
dont-break --top-downloads 10
// finds 10 most downloaded modules that depend on your module
// saves the list to .dont-break file
// tests found names
The found list of names overwrites .dont-break
file to avoid delay next time.