By: Dan Levy Follow me on twitter/github: @justsml
- Code like a story
- Eliminate ad hoc code
- Similar async & sync patterns (when possible)
- Uniform interfaces
- Well defined & changeable pathways
- Flat & highly modular
- Limit complexity score
- Immutable if possible
- Favor pure & stateless functions
For details, press DOWN arrow/key
For next rule, press RIGHT arrow/key +++ Code should be readable for the next human. It helps to keep function complexity low (Rule No. 7).
In my opinion, a pipeline-style pattern should be used by all Public methods in a module.
For details, press DOWN arrow/key
For next rule, press RIGHT arrow/key
+++
What is ad hoc logic?
It is a common violation of Single Responsibility (from SOLID). Identifying it only takes a little practice and discipline.
+++
+++
Martin Fowler on Function Length: +++ An increase in Function Length correlates with rising ad hoc logic. +++ A perfectly composed HoC/function is devoid of branching (
if
's).
If a function must be long HoC (a Higher Order Component), the lowest cost possible will always be a function chain
+++
To start, move your validation or data checks to their own functions. Give your instructions meaning.
These 4 'red flags' are very helpful in your search:
+++
- 10+ expressions/statements in a function
- Co-mingling
if
's with function calls inside huge 'super functions' - Any
let
/const
/var
mid-function definition - More than 1-3 lines before a
return
Bottom line: Avoid complex execution trees. Tiny functions usually always win.
For details, press DOWN arrow/key
For next rule, press RIGHT arrow/key
+++
This is not literally desireable everywhere. However there is something really useful about utility functions & Bluebird Promise's array methods...
+++
Can you make
isTest
function (next slide) async without affecting other code? +++
const leads = [...]
// can you make isTest async w/ no impl. changes?
const isTest = (x) => x.test === true
return leads.map(isTest)
+++
Bluebird Promises solve this by extending Array.prototype
methods into the "Promises" space.
+++
// Get a promise wrapped array
const leads = Promise.resolve([...])
// Here's an async version of isTest():
const isTestAsync = (x) => Promise.resolve(x.test)
return leads.map(isTestAsync)
For details, press DOWN arrow/key
+++
There are a few cases where 2+ parameter functions make sense. Here is a great example from NodeJS's FS API:
fs.renameSync(oldPath, newPath)
+++ In the context of dynamic languages, always prefer single argument funtions.
+++
A compelling path to reusable code. With a strong functional emphasis. the start of reusable code.
For details, press DOWN arrow/key
For next rule, press RIGHT arrow/key
+++
Here's how too many 'exit points' leads to a delicate & unreadable mess... [press down arrow] +++
+++
For every
if
,for
,while
,switch
,throw
orreturn
you add to your function, you layer on the pathways.
Avoid code soup.
For details, press DOWN arrow/key
For next rule, press RIGHT arrow/key
+++
lib
├── config.js
├── crypto.js
└── db.js
auth
├── splash.png
├── index.html.tmpl
└── index.js
dashboard
├── index.html.tmpl
└── index.js
server
├── websockets.js
├── routes.js
└── index.js
+++
This has the advantage of locating related assets and/or modules near where they are used.
7: Low Complexity Score
For details, press DOWN arrow/key For next rule, press RIGHT arrow/key
+++ Code can be argued over stylistic points. But cyclomatic complexity is a number representing (roughly) the # of branches in a block of code.
For details, press DOWN arrow/key For next rule, press RIGHT arrow/key +++
Aim for immutablity. It's not much different.
const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50
+++
It may be difficult at first adopting Immutable coding patterns. But it is well worth learning.
For details, press DOWN arrow/key For next rule, press RIGHT arrow/key
Background: Over (roughly) the last 10 years, I've been gradually adopting & experimenting with these rules in my own projects. Some rules benefit all languages. Some rules only make sense for dynamic languages. And some of my rules are quite upsetting to some (and w/o JavaScript, I'd agree).