Opinions are like assholes, every one has got one.
This one is mine.
Punctuation is a bikeshed. Put your semicolons, whitespace, and commas where you like them.
This post is concerned with higher-order style.
Don't do something complex just to make your api simpler. That will lead to confusing bugs, and necessitate reading your code, while also making reading your code harder. This will be frustrating for me, when I'm trying to use your module.
Example, avoid chaining DSLs.
This is bad:
thing.when('something').then(doThing)
It's not really obvious how when relates to dothing. It's better to pass in related things together.
thing.when('something', doThing)
//or maybe
thing({when: 'something', then: doThing})
Chaining where you simply return this
is acceptable.
if possible, make your code follow the APIs in node core. That way, I already know how to use your module. I don't even have to read the documentation. This is ideal.
If you don't do this, you need documentation. But even worse, is if have merely a similar API, but does not behave exactly the same, I will misuse your code, get an error and be forced to read your code. This will frustrate me.
if your can't follow an idiomatic API precisely, do something completely different.
ALWAYS PASS ERR IN CALLBACK, (an event listener is not a callback, so this doesn't apply in that case)
If you have a function called createServer
,
it should return a server, and it should have a listen
function.
createServer
should never start the server listening.
That should never happen until I call listen
.
a listen
function that takes something other than a port, a hostname, and a callback (like in socket.io) should be called something else.
my favorite API from node is Stream
(more on that later)
Functions are your most powerful weapon. In javascript you do not need fancy plugins. Allow users to extend of control your modules behaviour by passing in a simple function, with given arguments.
connect
is a good example of this.
don't optimise things that aren't gonna be bottlenecks, example; it's completely acceptable to read configuration files synchronously. because you only do it once, and you need to do it before anything else happens in the application.
it's something you have to read every time you respond to an http request, then it must by async.
use flow control over control flow
streams in node are one of the rare occasions when doing something the fast way is actually easier. SO USE THEM. not since bash has streaming been introduced into a high level language as nicely as it is in node.
compare downloading a file one step at a time, and streaming it:
//load the response entirely into memory
request ('http://slow.com', function (err, doc) {
//write the whole thing to disk
fs.writeFile(dest, doc, function (err) {
//done
})
})
now, stream it:
request('http://sweet.as') //read from the internet
.pipe(fs.createWriteStream(pathToFile)) //write to disk as data arrives.
.on('end', function () {
//done
})
the latter is preferred, the syntax is nicer, but more importantly, streams handle data in a smarter way.
they pass data along as it arrives, and can also tell the upstream to pause if they can't handle any more.
in the above example with the callbacks, the time it would take to complete is the time to request + the time to write to disk.
in the example with the streams, it will only take slightly longer than the time to request.
if the file is VERY LARGE this will be significant, also if it is VERY VERY LARGE it may not all fit in memory at once, but streaming will still work. the node.js team work very hard to make streams as fast as possible, and it's also easier, so use them.
node.js has the best package manager so far. This is not hyperbole. It's simple yet powerful. so split up every thing, this also makes it easier to test, and easier to extend.
I also like to separate layers,for example, if you are writing a service of some kind, write it to that you can test it entirely in memory, and then wrap it with layers that provide the network io.
The style of a line is easy to change. But the style of a module is much more important. This is something that I think is truly worthy of debate, because it actually makes a significant difference.
what are your opinions on high-level style?
I normally use comments when explaining a weird thing, or maybe marking something which was a painful lesson.
Other times I write comments while I'm figuring out what code i need to write (so it's stream of conciousnessy and I might just delete it later)
Comments are not documentation (and neither are tests)!
It was a long time since I wrote this, and so somethings would change...
I guess promises are okay now. well documented and consistent enough. (not that I use them though)
If I wrote this again, I think I'd talk about secondary conotations of code. When you read code, you gain meaning not just from what the code says, but also, what it doesn't say.
for example, named functions can be called from above or below.
if that is the only time
namedFunction
was used, then if it an anonymous function.then that would be immediatly obvious. This applies to scoping also. If you do use a named function once, if you define it in the narrowest scope, then it's obvious that it's not used by any other parts of the code.
On the other hand, if you define functions at the outer most scope, then it's obvious they do not depend on variables inside those scopes.
(though, it's pretty easy to see what local and non local variables they have... but that should only be an issue on long functions, which are bad for other reasons)
Also, some people like to use named functions after their code. I really hate this, although a number of coders that I respect do it. Dependencies go at the top! a function that you wrote that you are gonna call later is a function. I'd gain more information by scanning past those functions than I would from reading the name. naming is hard, so try to avoid naming if you can.