Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Applied clean coding in Node.js

Applied clean coding in Node.js

Writing clean code is what you must know and do in order to call yourself a professional developer. There is no reasonable excuse for doing anything less than your best.

Based on the blog post: https://blog.risingstack.com/javascript-clean-coding-best-practices-node-js-at-scale/

Avatar for András Tóth

András Tóth

May 03, 2017

Other Decks in Programming

Transcript

  1. How do we learn JavaScript? • we usually learn it

    by looking at code on the web • a lot can be achieved by using code from stackoverflow
  2. What does clean coding mean? Clean coding means that you

    write code for your later self and for your co-workers, and not for the machine.
  3. What does clean coding mean? You are working on a

    clean code when each routine you read turns out to be pretty much what you expected.
  4. Why should you care? “Lowering quality lengthens development time.” -

    First Law of Programming http://wiki.c2.com/?FirstLawOfProgramming
  5. What will we cover? 1. How to name the variables

    2. How to write and organize functions 3. Leveraging the new features of ES2015 / ES2016 to write nice asynchronous code 4. Code performance
  6. How to name the variables? let d let elapsed const

    ages = arr.map((i) => i.age) let nameString let theUsers • DON’T save keyboard strokes • DON’T add extra, unnecessary nouns • use intention-revealing names
  7. How to name the variables? let daysSinceModification const agesOfUsers =

    users.map((user) => user.age) • DON’T save keyboard strokes • DON’T add extra, unnecessary nouns • use intention-revealing names
  8. How to name the variables? let fName, lName let cntr

    const yyyymmdstr = moment().format('YYYY/MM/DD') • DON’T abbreviate when not justifiable • use meaningful and pronounceable names
  9. How to name the variables? let firstName, lastName let counter

    const currentDate = moment().format('YYYY/MM/DD') • DON’T abbreviate when not justifiable • use meaningful and pronounceable names
  10. function getUserHandler (req, res) { const { userId } =

    req.params ™™DIGDI@LP@MT knex('user') .where({ id: userId }) .first() .then((user) => res.json(user)) } How to write functions? • DON’T mix responsibilities • should do one thing on one level of abstraction
  11. ™™JPO@C<I?G@M function getUserHandler (req, res) { const { userId }

    = req.params User.getOne(userId) .then((user) => res.json(user)) } ™™N@MHJ?@G const tableName = 'user' const User = { getOne (userId) { return knex(tableName) .where({ id: userId }) .first() } } How to write functions? • DON’T mix responsibilities • should do one thing on one level of abstraction
  12. “Functions should do one thing. They should do it well.

    They should do it only.” - Robert C. Martin (Uncle Bob)
  13. How to write functions? getUserInfo() getClientData() fetchCustomerRecord() • DON’T mix

    synonyms • use the same vocabulary for the same type of variable
  14. How to write functions? getUser() • DON’T mix synonyms •

    use the same vocabulary for the same type of variable
  15. ™¥¥ ¥ IQDO@<I@RPN@MRDOCDON@H<DG<??M@NN ¥°K<M<H£OMDIB¤PN@M@H<DG<??M@NN ¥™ function inv (user) { ™¥DHKG@H@IO<ODJI¥™

    } How to write functions? • use long, descriptive names • the name and order of the arguments should communicate its intent
  16. function inviteUser (emailAddress) { ™¥DHKG@H@IO<ODJI¥™ } How to write functions?

    • use long, descriptive names • the name and order of the arguments should communicate its intent
  17. function getRegisteredUsers (fields, include, fromDate, toDate) {} getRegisteredUsers( ['firstName', 'lastName',

    'email'], ['invitedUsers'], '2016-09-26', '2016-12-13' ) How to write functions? • avoid long argument list • use object parameter when # arguments ≤ 2 (and destructing assignment)
  18. function getRegisteredUsers ({fields, include, fromDate, toDate}) {} getRegisteredUsers({ fields: ['firstName',

    'lastName', 'email'], include: ['invitedUsers'], fromDate: '2016-09-26', toDate: '2016-12-13' }) How to write functions? • avoid long argument list • use object parameter when # arguments ≤ 2 (and destructing assignment)
  19. function addItemToCart (cart, item, quantity) { quantity = quantity ||

    1 ™¥DHKG@H@IO<ODJI¥™ } How to write functions? • use default arguments instead of short circuiting or conditionals
  20. function addItemToCart (cart, item, quantity = 1) { ™¥DHKG@H@IO<ODJI¥™ }

    How to write functions? • use default arguments instead of short circuiting or conditionals
  21. function addItemToCart (cart, item, quantity = 1) { const alreadyInCart

    = cart.get(item.id) || 0 cart.set(item.id, alreadyInCart + quantity) item.totalSaleCount += quantity return cart } What’s wrong with this?
  22. function addItemToCart (cart, item, quantity = 1) { const alreadyInCart

    = cart.get(item.id) || 0 cart.set(item.id, alreadyInCart + quantity) item.totalSaleCount += quantity return cart } Avoid side effects with functions!
  23. function addItemToCart (cart, item, quantity = 1) { const cartCopy

    = new Map(cart) const alreadyInCart = cartCopy.get(item.id) || 0 cartCopy.set(item.id, alreadyInCart + quantity) return cartCopy } Avoid side effects with functions!
  24. ™™<?? O@HJ<MOŸ><MO†DO@H†LP<IODOT  ™™ŠÕ><MO‰<?? O@HŸDO@H†LP<IODOT const cart = new Map()

    Object.assign(cart, { addItem (item, quantity = 1) { const alreadyInCart = this.get(item.id) || 0 this.set(item.id, alreadyInCart + quantity) return this } }) Avoid side effects with functions!
  25. function getFullName (user) { return `${user.firstName} ${user.lastName}` } ™™… function

    renderEmailTemplate (user) { const fullName = getFullName(user) return `Dear ${fullName}, ...` } How to write functions? • organize your functions in a file according to the stepdown rule
  26. function renderEmailTemplate (user) { const fullName = getFullName(user) return `Dear

    ${fullName}, ...` } ™™… function getFullName (user) { return `${user.firstName} ${user.lastName}` } How to write functions? • organize your functions in a file according to the stepdown rule
  27. asyncFunc1((err1, result1) => { asyncFunc2(result1, (err2, result2) => { asyncFunc3(result2,

    (err3, result3) => { console.log(result3) }) }) }) How to write nice async code? • use Promises whenever you can • handling errors with callbacks is cumbersome • reading nested callbacks is hard
  28. asyncFuncPromise1() .then(asyncFuncPromise2) .then(asyncFuncPromise3) .then((result) => console.log(result)) .catch((err) => console.error(err)) How

    to write nice async code? • use Promises whenever you can • handling errors with callbacks is cumbersome • reading nested callbacks is hard
  29. function readJSONFile (filePath, callback) { fs.readFile(filePath, (err, data) => {

    if (err) { return callback(err) } try { callback(null, JSON.parse(data)) } catch (ex) { callback(ex) } }) } How to write nice async code?
  30. const fs = require('fs') const promisify = require('es6-promisify') const readFile

    = promisify(fs.readFile) function readJSONFile (filePath) { return readFile(filePath) .then((data) => JSON.parse(data)) } How to write nice async code? • use Promises whenever you can • you can convert callback APIs to Promise based (eg. with npm package es6-promisify)
  31. const co = require('co') const getExtractFromWikipedia = co.wrap(function * (title)

    { let body try { body = yield request({ ™¥K<M<H@O@MNomitted¥™ }) } catch (err) { console.error('getExtractFromWikipedia() error:', err) throw err } const extracts = Object.keys(body.query.pages) .map((key) => body.query.pages[key].extract) return extracts[0] })
  32. async function getExtractFromWikipedia (title) { let body try { body

    = await request({ ™¥K<M<H@O@MNJHDOO@?¥™ }) } catch (err) { console.error('getExtractFromWikipedia() error:', err) throw err } const extracts = Object.keys(body.query.pages) .map((key) => body.query.pages[key].extract) return extracts[0] } getExtractFromWikipedia('Robert Cecil Martin').then(console.log)
  33. ™™M@HJQ@OC@PN@M Users.delete(user.id) ™™DI>M@H@IO=TLP<IODOT itemCount += quantity What should be your

    comments about? • describe “why” you are doing something and not “what” • the code should tell by itself what it’s doing
  34. Avoid the JS hacks ™™>JIQ@MOOMDIBOJPH=@M const ageAsString = '98' const

    age = +ageAsString ™™>JIQ@MOOJJJG@<I const isThere = !!user ™™>C@>FDADO@HDNDIMM<T if (~registeredUsers.indexOf(user)) { ™™?JNJH@OCDIB‰‰‰ }
  35. Avoid the JS hacks ™™>JIQ@MOOMDIBOJPH=@M const ageAsString = '98' const

    age = Number(ageAsString) ™™>C@>FDADO@HDNDIMM<T if (registeredUsers.indexOf(user) > -1) {} ™™AMJHJ?@Á‰À if (registeredUsers.includes(user)) {}
  36. Use ESLint • A linter can catch a lot of

    programmer mistakes. • It enforces a consistent coding style. • Go with existing styles and modify them for your needs!
  37. Note on code performance • Code performance is the number

    one reason for writing “smart” code. • Optimize the code when you need to and refer to true impact instead of micro-benchmarks!
  38. Takeaways • Slowing down often helps to move faster. •

    Learn the clean coding guidelines and apply them. • Agree upon a universal coding guideline and style! • Use a linter to enforce the style & add it to the test/CI pipeline. • Embrace code reviews!