Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use individual args and validate them. #4

Merged
merged 6 commits into from
Dec 11, 2015
Merged

Use individual args and validate them. #4

merged 6 commits into from
Dec 11, 2015

Conversation

boennemann
Copy link
Member

An options object suggest that some arguments might not be required.
In the case of this module all arguments are required and not passing any of them leads to very unexpected and dangerous results.

Throws an Error when arguments are missing or of the wrong type, instead of ignoring or coercing.

An options object suggest that some arguments might not be required.
In the case of this module all arguments are required and not passing any of them leads to very unexpected and dangerous results.

BREAKING CHANGE: Instead of an options object the function is now expecting the arguments to be passed individually.
BREAKING CHANGE: Throws an Error when arguments are missing or of the wrong type, instead of ignoring or coercing.
@gr2m
Copy link
Member

gr2m commented Dec 10, 2015

I don’t agree with this, options are much more readable and self-explanatory, you don’t need to remember the order of arguments. And I’d argue that this is so low level and that it might be used excessively, so validation might be performance impact

@boennemann
Copy link
Member Author

This is not a matter of style or readability, but semantics. Options are modifiers to a certain operation.

a thing that is or may be chosen.

They are by definition optional and have a default that's going to happen when they aren't passed.

In this case we're looking at a function that converts a given input to a certain output. None of the arguments are optional, or have any defaults, in order for them to generate the correct output.

I agree that an options object is a way better choice if there are indeed options.

// bad API
PouchDB.replicate('a', 'b', true, false)
// good API
PouchDB.replicate('a', 'b', {
  live: true, 
  retry: false
})

// bad API
document.addEventListener('click', something, false)

This function is low level and critical for the operation it's going to be used for. That's exactly why it should provide the best error output possible, so users can catch potentially fatal mistakes. As it's the nature of hashing the output of this function doesn't give a human any clue to what could possibly be wrong. With the current implementation this is possible:

generateSessionID({timestamp: [], salt: {a: 1}})
// 'dW5kZWZpbmVkOjpr9KGRZVIFE_X2OK54FmNZH1xa3A'

In alignment with the UNIX philosophy this module should be doing exactly one thing, and it should be doing it well. That's why I would want to use a module for this kind of operation, so I don't have to care for all the edge-cases myself. I wouldn't describe the above behaviour as doing this well.

The aproba module is as simple and performant as it can get: https://github.com/iarna/aproba/blob/master/index.js I wouldn't make perf a concern unless it's really the bottleneck of an application.

@boennemann
Copy link
Member Author

To add to this: With the options object a simple typo leads to fatal results:

// secret misspelt
var sessionId = generateSessionId({
  username: 'pat',
  salt: '24eb90e9e1343977b8323857287ffca4',
  sercet: '78875068a1979fb910d5d8f37d316aa4',
  timestamp: 1449689785
})

// expected:  'cGF0OjU2Njg4MkI5OuY74QyC7HgrKeFv8sWuds_vNfRC'
// actual:    'cGF0OjU2Njg4MkI5OnxG0uI0oo54cXFuU8heYXgxse7s'

@gr2m
Copy link
Member

gr2m commented Dec 10, 2015

Thanks for the write up @boennemann, that is very helpful! I'm still not convinced though, it’s to easy to make the mistake of

generateSessionId(username, secret, salt, timestamp)

although salt should come 2nd, and secret 3rd.

You seem much more passionate about it than me though :) So I’d suggest, if there is no other opinion by next week, let’s merge it

@janl
Copy link
Member

janl commented Dec 10, 2015

What @boennemann says +1 :)

We could argue about timestamp being required, we could generate that inside the fun, so we are down to username, salt and secret and then good naming can help, how about this?

function generateSessionId(userName, userSalt, serverSecret) {}

@boennemann
Copy link
Member Author

According to @janl's suggestion I updated the argument names now, to make things even more unambiguous. I still kept the timestamp argument though, because I wanted the function to remain pure.

I agree that it's easy to mix up argument order, but (without further data to back this) I think it mostly happens when it's a user-facing API that's going to be used over and over again. Instead of looking up the documentation, or directly copying the example people try and start to remember things, and that's where the mistake happens. This is probably not the case for this function.

Also this is what my editor gave me without any extra config:
screen shot 2015-12-11 at 02 29 00
Tools can take care of it. (The slightly messed up type annotations only call for more validation ;))

@gr2m gr2m merged commit 3c66a31 into master Dec 11, 2015
@gr2m gr2m deleted the validate-args branch December 11, 2015 03:09
@gr2m
Copy link
Member

gr2m commented Dec 11, 2015

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants