60
Sharing is Caring! Patterns for JavaScript Library Design @maggiepint 1 / 60

Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Sharing is Caring!

Patterns for JavaScript Library Design

@maggiepint

1 / 60

Page 2: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Who am I?

2 / 60

Page 3: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Who am I?Semicolons/Tabs

3 / 60

Page 4: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Who am I?Crisis Management Engineer at MicrosoftA maintainer (not the author) of Moment.jsJS Foundation Representative to TC39Champion of Date rework in JavaScript (tell me your thoughts!)

Twitter: @maggiepint

Email: [email protected]

4 / 60

Page 5: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

A library is a bit of code that is useful when packaged up and distributed toother people.

This could be internal or external.

5 / 60

Page 6: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Libraries1. LoDash2. jQuery3. Q4. Moment5. Immutable6. Request

Not Libraries1. Express2. Angular3. Webpack

6 / 60

Page 7: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What we think having a library is like

7 / 60

Page 8: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What having a library is actually like

8 / 60

Page 9: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

9 / 60

Page 10: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

10 / 60

Page 11: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?

11 / 60

Page 12: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

12 / 60

Page 13: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

13 / 60

Page 14: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

Great Code!

14 / 60

Page 15: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

Great Code!

15 / 60

Page 16: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

Great Code!

Encourages functional programming practices

16 / 60

Page 17: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

Great Code!

Encourages functional programming practices

17 / 60

Page 18: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

Great Code!

Encourages functional programming practices

Amazing nodejs/webpack/babel/mocha/chai/phantom/sauce/istanbultoolchain!

18 / 60

Page 19: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

Great Code!

Encourages functional programming practices

Amazing nodejs/webpack/babel/mocha/chai/phantom/sauce/istanbultoolchain!

19 / 60

Page 20: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

What makes a library good?Small Size!

Great Code!

Encourages functional programming practices

Amazing nodejs/webpack/babel/mocha/chai/phantom/sauce/istanbultoolchain!

Ease of use

20 / 60

Page 21: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Ease of Use!

21 / 60

Page 22: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Nobody wants to learn your library.

22 / 60

Page 23: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

It's okay to make it simple.

23 / 60

Page 24: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Considerations1. Invocation2. Configuration3. Defaults4. Errors

24 / 60

Page 25: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Invocation

25 / 60

Page 26: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Static Invocation(Simplest Option)

// requestrequest('http://www.google.com', (err, resp, body) => {});

// lodashlet odds = _.filter([1,2,3,4,5], (n) => { return n % 2});

26 / 60

Page 27: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

The drawback:

let arr = [1,2,3,4,5];let sumOddsDoubled = _sum( _.map( _.filter(arr, (n) => { return n % 2}) , (n) => {return n *2 }));

27 / 60

Page 28: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Factory Function(Extends well)

// Qlet promise = Q.fcall();let promise = Q.all();let deferred = Q.defer();

// jQuerylet obj = $('.blue');

//momentlet myTime = moment();let myTime = moment.utc();

28 / 60

Page 29: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Chaining(AKA Fluent)

Q.fcall(promisedStep1).then(promisedStep2).then(promisedStep3).then(promisedStep4).then(function (value4) { // Do something with value4}).catch(function (error) { // Handle any error from all above steps}).done();

29 / 60

Page 30: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

moment().add(3, 'days').startOf('day').subtract(1, 'year');

30 / 60

Page 31: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

$('.blue').append('<p>these elements have a blue class</p>') .class('emphasize);

31 / 60

Page 32: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Con�guration

32 / 60

Page 33: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

First there was:

let dateString = 'May 1, 2017';moment(dateString);moment(dateString, 'MMM D, YYYY');

33 / 60

Page 34: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

First there was:

let dateString = 'May 1, 2017';moment(dateString);moment(dateString, 'MMM D, YYYY');

And then there was:

moment([2017,2,11]);moment(moment());moment(new Date());moment(dateString, 'MMM D, YYYY', 'en');moment(dateString, 'MMM D, YYYY', true);moment(dateString, 'MMM D, YYYY', 'en', true);moment(dateString, ['MMM D, YYYY', 'YYYY-MM-DD']);moment(dateString, ['MMM D, YYYY', 'YYYY-MM-DD'],'en');moment(dateString, ['MMM D, YYYY', 'YYYY-MM-DD'], true);moment(dateString, ['MMM D, YYYY', 'YYYY-MM-DD'],'en', true);

34 / 60

Page 35: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Options Objects(The ECMA402 Intl API)

let options = { hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short'};

let dateString = new Intl.DateTimeFormat('en-AU', options).format(date);

35 / 60

Page 36: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Options allow for pluggable business logiclet options = { statusCode: { 404: function() { alert( "page not found" ); }, 500: function() { throw 'the world has ended'; } }};

$.ajax(options);

36 / 60

Page 37: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Required paramaters �rst, then an optionsobject

37 / 60

Page 38: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Defaults

38 / 60

Page 39: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Plain HTTP Request with Node.JSlet http = require('http');http.get({ host: 'api.github.com', path: '/repos/timrwood/moment', headers: { 'User-Agent': 'request' } }, function(response) { // Continuously update stream with data let body = ''; response.on('data', function(d) { body += d; }); response.on('end', function() { console.log(response.statusCode); // 301 console.log(response.statusMessage); // Moved Permanently }); });

39 / 60

Page 40: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Sensible Defaults with Requestlet request = require('request');

let options = { url: 'https://api.github.com/repos/timrwood/moment', headers: { 'User-Agent': 'request' }};request(options, (err, resp, body) => { console.log(resp.request.uri.href); // https://api.github.com/repositories/1424470 console.log(resp.body); // {"id":1424470,"name":"moment","full_name":"moment/moment", ...});

40 / 60

Page 41: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Best By DefaultIf there is an obvious right answer, do it automatically.

41 / 60

Page 42: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

let dateTimeString = '2017-05-01 10:25:41.357-05:00';moment(dateTimeString).format('LLL');

1 May 2017 17:25

42 / 60

Page 43: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

WAT?

43 / 60

Page 44: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

let dateTimeString = '2017-05-01 10:25:41.357-05:00';moment(dateTimeString).format('LLL'); // 1 May 2017 17:25moment.utc(dateTimeString).format('LLL'); // 1 May 2017 15:25moment.parseZone(dateTimeString).format('LLL'); // 1 May 2017 10:25moment.tz(dateTimeString, 'America/New_York').format('LLL'); // 1 May 2017 11:25

44 / 60

Page 45: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

There is no best choice!

45 / 60

Page 46: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

let dateTimeString = '2017-05-01 10:25:41.357-05:00';moment.local(dateTimeString).format('LLL');

1 May 2017 17:25

46 / 60

Page 47: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Deafult When BestIf there are several likely behaviors, don't choose a deafult. It only leads to

confusion.

47 / 60

Page 48: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Errors

48 / 60

Page 49: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Any use of the JavaScript throw mechanism will raise an exceptionthat must be handled using try / catch or the Node.js process will

exit immediately.

The Node.js Docs

49 / 60

Page 50: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Myles cares a lot about errors.

50 / 60

Page 51: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

moment('asdfgh', 'DD/MM/YYYY').format() // invalid date

51 / 60

Page 52: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

moment('asdfgh', 'DD/MM/YYYY').format() // invalid date

Good! Bad user input doesn't crash the Node.jsprocess.

52 / 60

Page 53: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

moment().get('hours'); // 13

53 / 60

Page 54: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

moment().get('hours'); // 13

moment().get('huors'); // Moment {_isAMomentObject: true, _isUTC: false, _pf: ...

54 / 60

Page 55: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

moment().get('hours'); // 13

moment().get('huors'); // Moment {_isAMomentObject: true, _isUTC: false, _pf: ...

¯\(ツ)/¯

55 / 60

Page 56: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Throw on Obvious Developer Error

56 / 60

Page 57: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

const { Map } = require('immutable');const map1 = Map({ a: 1, b: 2, c: 3 });const map2 = map1.set('b', 50)let b1 = map1.get('b'); //2let b2 = map2.get('b')); // 50

57 / 60

Page 58: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

const { Map } = require('immutable');const map3 = Map(1); // TypeError: Expected Array or iterable object of [k, v] entries, // or keyed object: 1

58 / 60

Page 59: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Make it easy to use!Invocation

Start with basic static or factory invocationConsider a chaining API

Configuration

Use options objects for non-required configurations

Defaults

Best-by-defaultDeafult-when-best

Errors

Throw for obvious developer errors

59 / 60

Page 60: Sharing is Caring! - WordPress.comT h e d ra w b a c k : let arr = [1,2,3,4,5]; let sumOddsDoubled = _sum(_.map(_.filter(arr, (n) => { return n % 2}), (n) => {return n *2 })); 2

Share!

@maggiepint - [email protected] - https://www.maggiepint.com

60 / 60