Fix Broken links (KOREAN)

This commit is contained in:
ChangJoo Park
2019-05-07 16:09:56 +09:00
parent a1abdc2f8d
commit 095f1ab91d
32 changed files with 1346 additions and 19 deletions

View File

@ -0,0 +1,28 @@
# Discover errors and downtime using APM products
### One Paragraph Explainer
Exception != Error. Traditional error handling assumes the existence of Exception but application errors might come in the form of slow code paths, API downtime, lack of computational resources and more. This is where APM products come in handy as they allow to detect a wide variety of burried issues proactively with a minimal setup. Among the common features of APM products are for example alerting when the HTTP API returns errors, detect when the API response time drops below some threshold, detection of code smells, features to monitor server resources, operational intelligence dashboard with IT metrics and many other useful features. Most vendors offer a free plan.
### Wikipedia about APM
In the fields of information technology and systems management, Application Performance Management (APM) is the monitoring and management of performance and availability of software applications. APM strives to detect and diagnose complex application performance problems to maintain an expected level of service. APM is “the translation of IT metrics into business meaning ([i.e.] value)". Major products and segments.
### Understanding the APM marketplace
APM products constitute 3 major segments:
1. Website or API monitoring external services that constantly monitor uptime and performance via HTTP requests. Can be set up in few minutes. Following are few selected contenders: [Pingdom](https://www.pingdom.com/), [Uptime Robot](https://uptimerobot.com/), and [New Relic](https://newrelic.com/application-monitoring)
2. Code instrumentation product family which requires embedding an agent within the application to use features like slow code detection, exception statistics, performance monitoring and many more. Following are few selected contenders: New Relic, App Dynamics
3. Operational intelligence dashboard this line of products is focused on facilitating the ops team with metrics and curated content that helps to easily stay on top of application performance. This usually involves aggregating multiple sources of information (application logs, DB logs, servers log, etc) and upfront dashboard design work. Following are few selected contenders: [Datadog](https://www.datadoghq.com/), [Splunk](https://www.splunk.com/), [Zabbix](https://www.zabbix.com/)
### Example: UpTimeRobot.Com Website monitoring dashboard
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard")
### Example: AppDynamics.Com end to end monitoring combined with code instrumentation
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation")

View File

@ -0,0 +1,62 @@
# Use Async-Await or promises for async error handling
### One Paragraph Explainer
Callbacks dont scale well since most programmers are not familiar with them. They force to check errors all over, deal with nasty code nesting and make it difficult to reason about the code flow. Promise libraries like BlueBird, async, and Q pack a standard code style using RETURN and THROW to control the program flow. Specifically, they support the favorite try-catch error handling style which allows freeing the main code path from dealing with errors in every function
### Code Example using promises to catch errors
```javascript
doWork()
.then(doWork)
.then(doOtherWork)
.then((result) => doWork)
.catch((error) => {throw error;})
.then(verify);
```
### Anti pattern code example callback style error handling
```javascript
getData(someParameter, function(err, result) {
if(err !== null) {
// do something like calling the given callback function and pass the error
getMoreData(a, function(err, result) {
if(err !== null) {
// do something like calling the given callback function and pass the error
getMoreData(b, function(c) {
getMoreData(d, function(e) {
if(err !== null ) {
// you get the idea? 
}
})
});
}
});
}
});
```
### Blog Quote: "We have a problem with promises"
From the blog pouchdb.com
> ……And in fact, callbacks do something even more sinister: they deprive us of the stack, which is something we usually take for granted in programming languages. Writing code without a stack is a lot like driving a car without a brake pedal: you dont realize how badly you need it until you reach for it and its not there. The whole point of promises is to give us back the language fundamentals we lost when we went async: return, throw, and the stack. But you have to know how to use promises correctly in order to take advantage of them.
### Blog Quote: "The promises method is much more compact"
From the blog gosquared.com
> ………The promises method is much more compact, clearer and quicker to write. If an error or exception occurs within any of the ops it is handled by the single .catch() handler. Having this single place to handle all errors means you dont need to write error checking for each stage of the work.
### Blog Quote: "Promises are native ES6, can be used with generators"
From the blog StrongLoop
> ….Callbacks have a lousy error-handling story. Promises are better. Marry the built-in error handling in Express with promises and significantly lower the chances of an uncaught exception. Promises are native ES6, can be used with generators, and ES7 proposals like async/await through compilers like Babel
### Blog Quote: "All those regular flow control constructs you are used to are completely broken"
From the blog Bennos
> ……One of the best things about asynchronous, callback-based programming is that basically all those regular flow control constructs you are used to are completely broken. However, the one I find most broken is the handling of exceptions. Javascript provides a fairly familiar try…catch construct for dealing with exceptions. The problem with exceptions is that they provide a great way of short-cutting errors up a call stack, but end up being completely useless if the error happens on a different stack…

View File

@ -0,0 +1,62 @@
# Catch unhandled promise rejections
<br/><br/>
### One Paragraph Explainer
Typically, most of modern Node.js/Express application code runs within promises whether within the .then handler, a function callback or in a catch block. Surprisingly, unless a developer remembered to add a .catch clause, errors thrown at these places are not handled by the uncaughtException event-handler and disappear. Recent versions of Node added a warning message when an unhandled rejection pops, though this might help to notice when things go wrong but it's obviously not a proper error handling method. The straightforward solution is to never forget adding .catch clauses within each promise chain call and redirect to a centralized error handler. However, building your error handling strategy only on developers discipline is somewhat fragile. Consequently, its highly recommended using a graceful fallback and subscribe to `process.on(unhandledRejection, callback)` this will ensure that any promise error, if not handled locally, will get its treatment.
<br/><br/>
### Code example: these errors will not get caught by any error handler (except unhandledRejection)
```javascript
DAL.getUserById(1).then((johnSnow) => {
// this error will just vanish
if(johnSnow.isAlive == false)
throw new Error('ahhhh');
});
```
<br/><br/>
### Code example: Catching unresolved and rejected promises
```javascript
process.on('unhandledRejection', (reason, p) => {
// I just caught an unhandled promise rejection, since we already have fallback handler for unhandled errors (see below), let throw and let him handle that
throw reason;
});
process.on('uncaughtException', (error) => {
// I just received an error that was never handled, time to handle it and then decide whether a restart is needed
errorManagement.handler.handleError(error);
if (!errorManagement.handler.isTrustedError(error))
process.exit(1);
});
```
<br/><br/>
### Blog Quote: "If you can make a mistake, at some point you will"
From the blog James Nelson
> Lets test your understanding. Which of the following would you expect to print an error to the console?
```javascript
Promise.resolve(promised value).then(() => {
throw new Error(error);
});
Promise.reject(error value).catch(() => {
throw new Error(error);
});
new Promise((resolve, reject) => {
throw new Error(error);
});
```
> I dont know about you, but my answer is that Id expect all of them to print an error. However, the reality is that a number of modern JavaScript environments wont print errors for any of them.The problem with being human is that if you can make a mistake, at some point you will. Keeping this in mind, it seems obvious that we should design things in such a way that mistakes hurt as little as possible, and that means handling errors by default, not discarding them.

View File

@ -0,0 +1,83 @@
# Handle errors centrally. Not within middlewares
### One Paragraph Explainer
Without one dedicated object for error handling, greater are the chances of important errors hiding under the radar due to improper handling. The error handler object is responsible for making the error visible, for example by writing to a well-formatted logger, sending events to some monitoring product like [Sentry](https://sentry.io/), [Rollbar](https://rollbar.com/), or [Raygun](https://raygun.com/). Most web frameworks, like [Express](http://expressjs.com/en/guide/error-handling.html#writing-error-handlers), provide an error handling middleware mechanism. A typical error handling flow might be: Some module throws an error -> API router catches the error -> it propagates the error to the middleware (e.g. Express, KOA) who is responsible for catching errors -> a centralized error handler is called -> the middleware is being told whether this error is an untrusted error (not operational) so it can restart the app gracefully. Note that its a common, yet wrong, practice to handle errors within Express middleware doing so will not cover errors that are thrown in non-web interfaces.
### Code Example a typical error flow
```javascript
// DAL layer, we don't handle errors here
DB.addDocument(newCustomer, (error, result) => {
if (error)
throw new Error("Great error explanation comes here", other useful parameters)
});
// API route code, we catch both sync and async errors and forward to the middleware
try {
customerService.addNew(req.body).then((result) => {
res.status(200).json(result);
}).catch((error) => {
next(error)
});
}
catch (error) {
next(error);
}
// Error handling middleware, we delegate the handling to the centralized error handler
app.use(async (err, req, res, next) => {
const isOperationalError = await errorHandler.handleError(err);
if (!isOperationalError) {
next(err);
}
});
```
### Code example handling errors within a dedicated object
```javascript
module.exports.handler = new errorHandler();
function errorHandler() {
this.handleError = async function(err) {
await logger.logError(err);
await sendMailToAdminIfCritical;
await saveInOpsQueueIfCritical;
await determineIfOperationalError;
};
}
```
### Code Example Anti Pattern: handling errors within the middleware
```javascript
// middleware handling the error directly, who will handle Cron jobs and testing errors?
app.use((err, req, res, next) => {
logger.logError(err);
if (err.severity == errors.high) {
mailer.sendMail(configuration.adminMail, 'Critical error occured', err);
}
if (!err.isOperational) {
next(err);
}
});
```
### Blog Quote: "Sometimes lower levels cant do anything useful except propagate the error to their caller"
From the blog Joyent, ranked 1 for the keywords “Node.js error handling”
> …You may end up handling the same error at several levels of the stack. This happens when lower levels cant do anything useful except propagate the error to their caller, which propagates the error to its caller, and so on. Often, only the top-level caller knows what the appropriate response is, whether thats to retry the operation, report an error to the user, or something else. But that doesnt mean you should try to report all errors to a single top-level callback, because that callback itself cant know in what context the error occurred…
### Blog Quote: "Handling each err individually would result in tremendous duplication"
From the blog JS Recipes ranked 17 for the keywords “Node.js error handling”
> ……In Hackathon Starter api.js controller alone, there are over 79 occurrences of error objects. Handling each err individually would result in a tremendous amount of code duplication. The next best thing you can do is to delegate all error handling logic to an Express middleware…
### Blog Quote: "HTTP errors have no place in your database code"
From the blog Daily JS ranked 14 for the keywords “Node.js error handling”
> ……You should set useful properties in error objects, but use such properties consistently. And, dont cross the streams: HTTP errors have no place in your database code. Or for browser developers, Ajax errors have a place in the code that talks to the server, but not code that processes Mustache templates…

View File

@ -0,0 +1,52 @@
# Document API errors using Swagger or GraphQL
### One Paragraph Explainer
REST APIs return results using HTTP status codes, its absolutely required for the API user to be aware not only about the API schema but also about potential errors the caller may then catch an error and tactfully handle it. For example, your API documentation might state in advance that HTTP status 409 is returned when the customer name already exists (assuming the API register new users) so the caller can correspondingly render the best UX for the given situation. Swagger is a standard that defines the schema of API documentation offering an eco-system of tools that allow creating documentation easily online, see print screens below
If you have already adopted GraphQL for your API endpoints, your schema already contains strict guarantees as to what errors should look like ([outlined in the spec](https://facebook.github.io/graphql/June2018/#sec-Errors)) and how they should be handled by your client-side tooling. In addition, you can also supplement them with comment-based documentation.
### GraphQL Error Example
> This example uses [SWAPI](https://graphql.org/swapi-graphql), the Star Wars API.
```graphql
# should fail because id is not valid
{
film(id: "1ZmlsbXM6MQ==") {
title
}
}
```
```json
{
"errors": [
{
"message": "No entry in local cache for https://swapi.co/api/films/.../",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"film"
]
}
],
"data": {
"film": null
}
}
```
### Blog Quote: "You have to tell your callers what errors can happen"
From the blog Joyent, ranked 1 for the keywords “Node.js logging”
> Weve talked about how to handle errors, but when youre writing a new function, how do you deliver errors to the code that called your function? …If you dont know what errors can happen or dont know what they mean, then your program cannot be correct except by accident. So if youre writing a new function, you have to tell your callers what errors can happen and what they mean…
### Useful Tool: Swagger Online Documentation Creator
![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling")

View File

@ -0,0 +1,47 @@
# Fail fast, validate arguments using a dedicated library
### One Paragraph Explainer
We all know how checking arguments and failing fast is important to avoid hidden bugs (see anti-pattern code example below). If not, read about explicit programming and defensive programming. In reality, we tend to avoid it due to the annoyance of coding it (e.g. think of validating hierarchical JSON object with fields like email and dates) libraries like Joi and Validator turn this tedious task into a breeze.
### Wikipedia: Defensive Programming
Defensive programming is an approach to improve software and source code, in terms of General quality reducing the number of software bugs and problems. Making the source code comprehensible the source code should be readable and understandable so it is approved in a code audit. Making the software behave in a predictable manner despite unexpected inputs or user actions.
### Code example: validating complex JSON input using Joi
```javascript
var memberSchema = Joi.object().keys({
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
birthyear: Joi.number().integer().min(1900).max(2013),
email: Joi.string().email()
});
function addNewMember(newMember) {
// assertions come first
Joi.assert(newMember, memberSchema); //throws if validation fails
// other logic here
}
```
### Anti-pattern: no validation yields nasty bugs
```javascript
// if the discount is positive let's then redirect the user to print his discount coupons
function redirectToPrintDiscount(httpResponse, member, discount) {
if (discount != 0) {
httpResponse.redirect(`/discountPrintView/${member.id}`);
}
}
redirectToPrintDiscount(httpResponse, someMember);
// forgot to pass the parameter discount, why the heck was the user redirected to the discount screen?
```
### Blog Quote: "You should throw these errors immediately"
From the blog: Joyent
> A degenerate case is where someone calls an asynchronous function but doesnt pass a callback. You should throw these errors immediately since the program is broken and the best chance of debugging it involves getting at least a stack trace and ideally a core file at the point of the error. To do this, we recommend validating the types of all arguments at the start of the function.

View File

@ -0,0 +1,54 @@
# Distinguish operational vs programmer errors
### One Paragraph Explainer
Distinguishing the following two error types will minimize your app downtime and helps avoid crazy bugs: Operational errors refer to situations where you understand what happened and the impact of it for example, a query to some HTTP service failed due to connection problem. On the other hand, programmer errors refer to cases where you have no idea why and sometimes where an error came from it might be some code that tried to read an undefined value or DB connection pool that leaks memory. Operational errors are relatively easy to handle usually logging the error is enough. Things become hairy when a programmer error pops up, the application might be in an inconsistent state and theres nothing better you can do than to restart gracefully
### Code Example marking an error as operational (trusted)
```javascript
// marking an error object as operational
const myError = new Error("How can I add new product when no value provided?");
myError.isOperational = true;
// or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
class AppError {
constructor (commonType, description, isOperational) {
Error.call(this);
Error.captureStackTrace(this);
this.commonType = commonType;
this.description = description;
this.isOperational = isOperational;
}
};
throw new AppError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);
```
### Blog Quote: "Programmer errors are bugs in the program"
From the blog, Joyent ranked 1 for the keywords “Node.js error handling”
> …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error…
### Blog Quote: "No safe way to leave without creating some undefined brittle state"
From Node.js official documentation
> …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error while letting the others finish in their normal time, and stop listening for new requests in that worker.
### Blog Quote: "Otherwise you risk the state of your application"
From the blog, debugable.com ranked 3 for the keywords “Node.js uncaught exception”
> …So, unless you really know what you are doing, you should perform a graceful restart of your service after receiving an “uncaughtException” exception event. Otherwise, you risk the state of your application, or that of 3rd party libraries to become inconsistent, leading to all kinds of crazy bugs…
### Blog Quote: "There are three schools of thoughts on error handling"
From the blog: JS Recipes
> …There are primarily three schools of thoughts on error handling:
1. Let the application crash and restart it.
2. Handle all possible errors and never crash.
3. A balanced approach between the two

View File

@ -0,0 +1,51 @@
# Exit the process gracefully when a stranger comes to town
### One Paragraph Explainer
Somewhere within your code, an error handler object is responsible for deciding how to proceed when an error is thrown if the error is trusted (i.e. operational error, see further explanation within best practice #3) then writing to log file might be enough. Things get hairy if the error is not familiar this means that some component might be in a faulty state and all future requests are subject to failure. For example, assuming a singleton, stateful token issuer service that threw an exception and lost its state from now it might behave unexpectedly and cause all requests to fail. Under this scenario, kill the process and use a Restarter tool (like Forever, PM2, etc) to start over with a clean state.
### Code example: deciding whether to crash
```javascript
// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
process.on('uncaughtException', function(error) {
errorManagement.handler.handleError(error);
if(!errorManagement.handler.isTrustedError(error))
process.exit(1)
});
// centralized error handler encapsulates error-handling related logic
function errorHandler() {
this.handleError = function (error) {
return logger.logError(err)
.then(sendMailToAdminIfCritical)
.then(saveInOpsQueueIfCritical)
.then(determineIfOperationalError);
}
this.isTrustedError = function (error) {
return error.isOperational;
}
}
```
### Blog Quote: "The best way is to crash"
From the blog Joyent
> …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error…
### Blog Quote: "There are three schools of thoughts on error handling"
From the blog: JS Recipes
> …There are primarily three schools of thoughts on error handling:
1. Let the application crash and restart it.
2. Handle all possible errors and never crash.
3. A balanced approach between the two
### Blog Quote: "No safe way to leave without creating some undefined brittle state"
From Node.js official documentation
> …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error while letting the others finish in their normal time, and stop listening for new requests in that worker.

View File

@ -0,0 +1,38 @@
# Test error flows using your favorite test framework
### One Paragraph Explainer
Testing happy paths is no better than testing failures. Good testing code coverage demands to test exceptional paths. Otherwise, there is no trust that exceptions are indeed handled correctly. Every unit testing framework, like [Mocha](https://mochajs.org/) & [Chai](http://chaijs.com/), supports exception testing (code examples below). If you find it tedious to test every inner function and exception you may settle with testing only REST API HTTP errors.
### Code example: ensuring the right exception is thrown using Mocha & Chai
```javascript
describe("Facebook chat", () => {
it("Notifies on new chat message", () => {
var chatService = new chatService();
chatService.participants = getDisconnectedParticipants();
expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError);
});
});
```
### Code example: ensuring API returns the right HTTP error code
```javascript
it("Creates new Facebook group", function (done) {
var invalidGroupInfo = {};
httpRequest({
method: 'POST',
uri: "facebook.com/api/groups",
resolveWithFullResponse: true,
body: invalidGroupInfo,
json: true
}).then((response) => {
// if we were to execute the code in this block, no error was thrown in the operation above
}).catch(function (response) {
expect(400).to.equal(response.statusCode);
done();
});
});
```

View File

@ -0,0 +1,54 @@
# Use a mature logger to increase errors visibility
### One Paragraph Explainer
We all love console.log but obviously, a reputable and persistent logger like [Winston][winston], [Bunyan][bunyan] (highly popular) or [Pino][pino] (the new kid in town which is focused on performance) is mandatory for serious projects. A set of practices and tools will help to reason about errors much quicker (1) log frequently using different levels (debug, info, error), (2) when logging, provide contextual information as JSON objects, see example below. (3) watch and filter logs using a log querying API (built-in in most loggers) or a log viewer software
(4) Expose and curate log statement for the operation team using operational intelligence tools like Splunk
[winston]: https://www.npmjs.com/package/winston
[bunyan]: https://www.npmjs.com/package/bunyan
[pino]: https://www.npmjs.com/package/pino
### Code Example Winston Logger in action
```javascript
// your centralized logger object
var logger = new winston.Logger({
level: 'info',
transports: [
new (winston.transports.Console)()
]
});
// custom code somewhere using the logger
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });
```
### Code Example Querying the log folder (searching for entries)
```javascript
var options = {
from: new Date - 24 * 60 * 60 * 1000,
until: new Date,
limit: 10,
start: 0,
order: 'desc',
fields: ['message']
};
// Find items logged between today and yesterday.
winston.query(options, function (err, results) {
// execute callback with results
});
```
### Blog Quote: "Logger Requirements"
From the blog Strong Loop
> Lets identify a few requirements (for a logger):
1. Timestamp each log line. This one is pretty self-explanatory you should be able to tell when each log entry occurred.
2. Logging format should be easily digestible by humans as well as machines.
3. Allows for multiple configurable destination streams. For example, you might be writing trace logs to one file but when an error is encountered, write to the same file, then into error file and send an email at the same time…

View File

@ -0,0 +1,81 @@
# Use only the built-in Error object
### One Paragraph Explainer
The permissive nature of JS along with its variety code-flow options (e.g. EventEmitter, Callbacks, Promises, etc) pushes to great variance in how developers raise errors some use strings, other define their own custom types. Using Node.js built-in Error object helps to keep uniformity within your code and with 3rd party libraries, it also preserves significant information like the StackTrace. When raising the exception, its usually a good practice to fill it with additional contextual properties like the error name and the associated HTTP error code. To achieve this uniformity and practices, consider extending the Error object with additional properties, see code example below
### Code Example doing it right
```javascript
// throwing an Error from typical function, whether sync or async
if(!productToAdd)
throw new Error("How can I add new product when no value provided?");
// 'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
// 'throwing' an Error from a Promise
const addProduct = async (productToAdd) => {
try {
const existingProduct = await DAL.getProduct(productToAdd.id);
if (existingProduct !== null) {
throw new Error("Product already exists!");
}
} catch (err) {
// ...
}
}
```
### Code example Anti Pattern
```javascript
// throwing a string lacks any stack trace information and other important data properties
if(!productToAdd)
throw ("How can I add new product when no value provided?");
```
### Code example doing it even better
```javascript
// centralized error object that derives from Nodes Error
function AppError(name, httpCode, description, isOperational) {
Error.call(this);
Error.captureStackTrace(this);
this.name = name;
//...other properties assigned here
};
AppError.prototype.__proto__ = Error.prototype;
module.exports.AppError = AppError;
// client throwing an exception
if(user == null)
throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
```
### Blog Quote: "I dont see the value in having lots of different types"
From the blog, Ben Nadel ranked 5 for the keywords “Node.js error object”
>…”Personally, I dont see the value in having lots of different types of error objects JavaScript, as a language, doesnt seem to cater to Constructor-based error-catching. As such, differentiating on an object property seems far easier than differentiating on a Constructor type…
### Blog Quote: "A string is not an error"
From the blog, devthought.com ranked 6 for the keywords “Node.js error object”
> …passing a string instead of an error results in reduced interoperability between modules. It breaks contracts with APIs that might be performing `instanceof` Error checks, or that want to know more about the error. Error objects, as well see, have very interesting properties in modern JavaScript engines besides holding the message passed to the constructor…
### Blog Quote: "Inheriting from Error doesnt add too much value"
From the blog machadogj
> …One problem that I have with the Error class is that is not so simple to extend. Of course, you can inherit the class and create your own Error classes like HttpError, DbError, etc. However, that takes time and doesnt add too much value unless you are doing something with types. Sometimes, you just want to add a message and keep the inner error, and sometimes you might want to extend the error with parameters, and such…
### Blog Quote: "All JavaScript and System errors raised by Node.js inherit from Error"
From Node.js official documentation
> …All JavaScript and System errors raised by Node.js inherit from, or are instances of, the standard JavaScript Error class and are guaranteed to provide at least the properties available on that class. A generic JavaScript Error object that does not denote any specific circumstance of why the error occurred. Error objects capture a “stack trace” detailing the point in the code at which the Error was instantiated, and may provide a text description of the error. All errors generated by Node.js, including all System and JavaScript errors, will either be instances of or inherit from, the Error class…

View File

@ -0,0 +1,20 @@
# Use an LTS release of Node.js in production
### One Paragraph Explainer
Ensure you are using an LTS(Long Term Support) version of Node.js in production to receive critical bug fixes, security updates and performance improvements.
LTS versions of Node.js are supported for at least 18 months and are indicated by even version numbers (e.g. 4, 6, 8). They're best for production since the LTS release line is focussed on stability and security, whereas the 'Current' release line has a shorter lifespan and more frequent updates to the code. Changes to LTS versions are limited to bug fixes for stability, security updates, possible npm updates, documentation updates and certain performance improvements that can be demonstrated to not break existing applications.
<br/><br/>
### Read on
🔗 [Node.js release definitions](https://nodejs.org/en/about/releases/)
🔗 [Node.js release schedule](https://github.com/nodejs/Release)
🔗 [Essential Steps: Long Term Support for Node.js by Rod Vagg](https://medium.com/@nodesource/essential-steps-long-term-support-for-node-js-8ecf7514dbd)
> ...the schedule of incremental releases within each of these will be driven by the availability of bug fixes, security fixes, and other small but important changes. The focus will be on stability, but stability also includes minimizing the number of known bugs and staying on top of security concerns as they arise.
<br/><br/>

View File

@ -0,0 +1,25 @@
# Sure user experience with APM products
<br/><br/>
### One Paragraph Explainer
APM (application performance monitoring) refers to a family of products that aims to monitor application performance from end to end, also from the customer perspective. While traditional monitoring solutions focus on Exceptions and standalone technical metrics (e.g. error tracking, slow server endpoints, etc), in the real world our app might create disappointed users without any code exceptions, for example, if some middleware service performed real slow. APM products measure the user experience from end to end, for example, given a system that encompasses frontend UI and multiple distributed services some APM products can tell how fast a transaction that spans multiple tiers last. It can tell whether the user experience is solid and point to the problem. This attractive offering comes with a relatively high price tag hence its recommended for large-scale and complex products that require going beyond straightforward monitoring.
<br/><br/>
### APM example a commercial product that visualizes cross-service app performance
![APM example](/assets/images/apm1.png "APM example")
<br/><br/>
### APM example a commercial product that emphasizes the user experience score
![APM example](/assets/images/apm2.png "APM example")
<br/><br/>
### APM example a commercial product that highlights slow code paths
![APM example](/assets/images/apm3.png "APM example")

View File

@ -0,0 +1,38 @@
# Assign TransactionId to each log statement
<br/><br/>
### One Paragraph Explainer
A typical log is a warehouse of entries from all components and requests. Upon detection of some suspicious line or error, it becomes hairy to match other lines that belong to the same specific flow (e.g. the user “John” tried to buy something). This becomes even more critical and challenging in a microservice environment when a request/transaction might span across multiple computers. Address this by assigning a unique transaction identifier value to all the entries from the same request so when detecting one line one can copy the id and search for every line that has similar transaction Id. However, achieving this In Node is not straightforward as a single thread is used to serve all requests consider using a library that that can group data on the request level see code example on the next slide. When calling other microservice, pass the transaction Id using an HTTP header like “x-transaction-id” to keep the same context.
<br/><br/>
### Code example: typical Express configuration
```javascript
// when receiving a new request, start a new isolated context and set a transaction Id. The following example is using the npm library continuation-local-storage to isolate requests
const { createNamespace } = require('continuation-local-storage');
var session = createNamespace('my session');
router.get('/:id', (req, res, next) => {
session.set('transactionId', 'some unique GUID');
someService.getById(req.params.id);
logger.info('Starting now to get something by Id');
});
// Now any other service or components can have access to the contextual, per-request, data
class someService {
getById(id) {
logger.info(Starting to get something by Id);
// other logic comes here
}
}
// The logger can now append the transaction-id to each entry so that entries from the same request will have the same value
class logger {
info (message)
{console.log(`${message} ${session.get('transactionId')}`);}
}
```

View File

@ -0,0 +1,42 @@
# Be stateless, kill your Servers almost every day
<br/><br/>
### One Paragraph Explainer
Have you ever encountered a severe production issue where one server was missing some piece of configuration or data? That is probably due to some unnecessary dependency on some local asset that is not part of the deployment. Many successful products treat servers like a phoenix bird it dies and is reborn periodically without any damage. In other words, a server is just a piece of hardware that executes your code for some time and is replaced after that.
This approach
- allows scaling by adding and removing servers dynamically without any side-effects.
- simplifies the maintenance as it frees our mind from evaluating each server state.
<br/><br/>
### Code example: anti-patterns
```javascript
// Typical mistake 1: saving uploaded files locally on a server
var multer = require('multer'); // express middleware for handling multipart uploads
var upload = multer({ dest: 'uploads/' });
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {});
// Typical mistake 2: storing authentication sessions (passport) in a local file or memory
var FileStore = require('session-file-store')(session);
app.use(session({
store: new FileStore(options),
secret: 'keyboard cat'
}));
// Typical mistake 3: storing information on the global object
Global.someCacheLike.result = { somedata };
```
<br/><br/>
### What Other Bloggers Say
From the blog [Martin Fowler](https://martinfowler.com/bliki/PhoenixServer.html):
> ...One day I had this fantasy of starting a certification service for operations. The certification assessment would consist of a colleague and I turning up at the corporate data center and setting about critical production servers with a baseball bat, a chainsaw, and a water pistol. The assessment would be based on how long it would take for the operations team to get all the applications up and running again. This may be a daft fantasy, but theres a nugget of wisdom here. While you should forego the baseball bats, it is a good idea to virtually burn down your servers at regular intervals. A server should be like a phoenix, regularly rising from the ashes...
<br/><br/>

View File

@ -0,0 +1,45 @@
# Create a maintenance endpoint
<br/><br/>
### One Paragraph Explainer
A maintenance endpoint is a highly secure HTTP API that is part of the app code and its purpose is to be used by the ops/production team to monitor and expose maintenance functionality. For example, it can return a heap dump (memory snapshot) of the process, report whether there are some memory leaks and even allow to execute REPL commands directly. This endpoint is needed where the conventional DevOps tools (monitoring products, logs, etc) fail to gather some specific type of information or you choose not to buy/install such tools. The golden rule is using professional and external tools for monitoring and maintaining the production, these are usually more robust and accurate. That said, there are likely to be cases where the generic tools will fail to extract information that is specific to Node or to your app for example, should you wish to generate a memory snapshot at the moment GC completed a cycle few npm libraries will be glad to perform this for you but popular monitoring tools will likely miss this functionality. It is important to keep this endpoint private and accessibly only by admins because it can become a target of a DDOS attack.
<br/><br/>
### Code example: generating a heap dump via code
```javascript
const heapdump = require('heapdump');
// Check if request is authorized
function isAuthorized(req) {
// ...
}
router.get('/ops/heapdump', (req, res, next) => {
if (!isAuthorized(req)) {
return res.status(403).send('You are not authorized!');
}
logger.info('About to generate heapdump');
heapdump.writeSnapshot((err, filename) => {
console.log('heapdump file is ready to be sent to the caller', filename);
fs.readFile(filename, "utf-8", (err, data) => {
res.end(data);
});
});
});
```
<br/><br/>
### Recommended Resources
[Getting your Node.js app production ready (Slides)](http://naugtur.pl/pres3/node2prod)
▶ [Getting your Node.js app production ready (Video)](https://www.youtube.com/watch?v=lUsNne-_VIk)
![Getting your Node.js app production ready](/assets/images/createmaintenanceendpoint1.png "Getting your Node.js app production ready")

View File

@ -0,0 +1,51 @@
# Delegate anything possible (e.g. static content, gzip) to a reverse proxy
<br/><br/>
### One Paragraph Explainer
Its very tempting to cargo-cult Express and use its rich middleware offering for networking related tasks like serving static files, gzip encoding, throttling requests, SSL termination, etc. This is a performance kill due to its single threaded model which will keep the CPU busy for long periods (Remember, Nodes execution model is optimized for short tasks or async IO related tasks). A better approach is to use a tool that expertise in networking tasks the most popular are nginx and HAproxy which are also used by the biggest cloud vendors to lighten the incoming load on node.js processes.
<br/><br/>
### Nginx Config Example Using nginx to compress server responses
```nginx
# configure gzip compression
gzip on;
gzip_comp_level 6;
gzip_vary on;
# configure upstream
upstream myApplication {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 64;
}
#defining web server
server {
# configure server with ssl and error pages
listen 80;
listen 443 ssl;
ssl_certificate /some/location/sillyfacesociety.com.bundle.crt;
error_page 502 /errors/502.html;
# handling static content
location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) {
root /usr/local/silly_face_society/node/public;
access_log off;
expires max;
}
```
<br/><br/>
### What Other Bloggers Say
* From the blog [Mubaloo](http://mubaloo.com/best-practices-deploying-node-js-applications):
> …Its very easy to fall into this trap You see a package like Express and think “Awesome! Lets get started” you code away and youve got an application that does what you want. This is excellent and, to be honest, youve won a lot of the battle. However, you will lose the war if you upload your app to a server and have it listen on your HTTP port because youve forgotten a very crucial thing: Node is not a web server. **As soon as any volume of traffic starts to hit your application, youll notice that things start to go wrong: connections are dropped, assets stop being served or, at the very worst, your server crashes. What youre doing is attempting to have Node deal with all of the complicated things that a proven web server does really well. Why reinvent the wheel?**
> **This is just for one request, for one image and bearing in mind this is the memory that your application could be used for important stuff like reading a database or handling complicated logic; why would you cripple your application for the sake of convenience?**
* From the blog [Argteam](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load):
> Although express.js has built-in static file handling through some connect middleware, you should never use it. **Nginx can do a much better job of handling static files and can prevent requests for non-dynamic content from clogging our node processes**…

View File

@ -0,0 +1,20 @@
# Use tools that automatically detect vulnerable dependencies
<br/><br/>
### One Paragraph Explainer
Modern Node applications have tens and sometimes hundreds of dependencies. If any of the dependencies
you use has a known security vulnerability your app is vulnerable as well.
The following tools automatically check for known security vulnerabilities in your dependencies:
- [npm audit](https://docs.npmjs.com/cli/audit) - npm audit
- [snyk](https://snyk.io/) - Continuously find & fix vulnerabilities in your dependencies
<br/><br/>
### What Other Bloggers Say
From the [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-one-security/) blog:
> ...Using to manage your applications dependencies is powerful and convenient. But the packages that you use may contain critical security vulnerabilities that could also affect your application. The security of your app is only as strong as the “weakest link” in your dependencies. Fortunately, there are two helpful tools you can use to ensure the third-party packages you use: nsp and requireSafe. These two tools do largely the same thing, so using both might be overkill, but “better safe than sorry” are words to live by when it comes to security...

View File

@ -0,0 +1,45 @@
# Get your frontend assets out of Node
<br/><br/>
### One Paragraph Explainer
In a classic web app the backend serves the frontend/graphics to the browser, a very common approach in the Nodes world is to use Express static middleware for streamlining static files to the client. BUT Node is not a typical webapp as it utilizes a single thread that is not optimized to serve many files at once. Instead, consider using a reverse proxy (e.g. nginx, HAProxy), cloud storage or CDN (e.g. AWS S3, Azure Blob Storage, etc) that utilizes many optimizations for this task and gain much better throughput. For example, specialized middleware like nginx embodies direct hooks between the file system and the network card and uses a multi-threaded approach to minimize intervention among multiple requests.
Your optimal solution might wear one of the following forms:
1. Using a reverse proxy your static files will be located right next to your Node application, only requests to the static files folder will be served by a proxy that sits in front of your Node app such as nginx. Using this approach, your Node app is responsible deploying the static files but not to serve them. Your frontends colleague will love this approach as it prevents cross-origin-requests from the frontend.
2. Cloud storage your static files will NOT be part of your Node app content, they will be uploaded to services like AWS S3, Azure BlobStorage, or other similar services that were born for this mission. Using this approach, your Node app is not responsible deploying the static files neither to serve them, hence a complete decoupling is drawn between Node and the Frontend which is anyway handled by different teams.
<br/><br/>
### Configuration example: typical nginx configuration for serving static files
```nginx
# configure gzip compression
gzip on;
keepalive 64;
# defining web server
server {
listen 80;
listen 443 ssl;
# handle static content
location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) {
root /usr/local/silly_face_society/node/public;
access_log off;
expires max;
}
```
<br/><br/>
### What Other Bloggers Say
From the blog [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-two-performance-and-reliability/):
>…In development, you can use [res.sendFile()](http://expressjs.com/4x/api.html#res.sendFile) to serve static files. But dont do this in production, because this function has to read from the file system for every file request, so it will encounter significant latency and affect the overall performance of the app. Note that res.sendFile() is not implemented with the sendfile system call, which would make it far more efficient. Instead, use serve-static middleware (or something equivalent), that is optimized for serving files for Express apps. An even better option is to use a reverse proxy to serve static files; see Use a reverse proxy for more information…
<br/><br/>

View File

@ -0,0 +1,17 @@
# Guard and restart your process upon failure (using the right tool)
<br/><br/>
### One Paragraph Explainer
At the base level, Node processes must be guarded and restarted upon failures. Simply put, for small apps and those who dont use containers tools like [PM2](https://www.npmjs.com/package/pm2-docker) are perfect as they bring simplicity, restarting capabilities and also rich integration with Node. Others with strong Linux skills might use systemd and run Node as a service. Things get more interesting for apps that use Docker or any container technology since those are usually accompanied by cluster management and orchestration tools (e.g. [AWS ECS](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html), [Kubernetes](https://kubernetes.io/), etc) that deploy, monitor and heal containers. Having all those rich cluster management features including container restart, why mess up with other tools like PM2? Theres no bulletproof answer. There are good reasons to keep PM2 within containers (mostly its containers specific version [pm2-docker](https://www.npmjs.com/package/pm2-docker)) as the first guarding tier its much faster to restart a process and provide Node-specific features like flagging to the code when the hosting container asks to gracefully restart. Other might choose to avoid unnecessary layers. To conclude this write-up, no solution suits them all and getting to know the options is the important thing
<br/><br/>
### What Other Bloggers Say
* From the [Express Production Best Practices](https://expressjs.com/en/advanced/best-practice-performance.html):
> ... In development, you started your app simply from the command line with node server.js or something similar. **But doing this in production is a recipe for disaster. If the app crashes, it will be offline** until you restart it. To ensure your app restarts if it crashes, use a process manager. A process manager is a “container” for applications that facilitate deployment, provides high availability, and enables you to manage the application at runtime.
* From the Medium blog post [Understanding Node Clustering](https://medium.com/@CodeAndBiscuits/understanding-nodejs-clustering-in-docker-land-64ce2306afef#.cssigr5z3):
> ... Understanding Node.js Clustering in Docker-Land “Docker containers are streamlined, lightweight virtual environments, designed to simplify processes to their bare minimum. Processes that manage and coordinate their own resources are no longer as valuable. **Instead, management stacks like Kubernetes, Mesos, and Cattle have popularized the concept that these resources should be managed infrastructure-wide**. CPU and memory resources are allocated by “schedulers”, and network resources are managed by stack-provided load balancers.

View File

@ -0,0 +1,69 @@
# Lock dependencies
<br/><br/>
### One Paragraph Explainer
Your code depends on many external packages, lets say it requires and use momentjs-2.1.4, then by default when you deploy to production npm might fetch momentjs 2.1.5 which unfortunately brings some new bugs to the table. Using npm config files and the argument ```save-exact=true``` instructs npm to refer to the *exact* same version that was installed so the next time you run ```npm install``` (in production or within a Docker container you plan to ship forward for testing) the same dependent version will be fetched. An alternative and popular approach is using a `.shrinkwrap` file (easily generated using npm) that states exactly which packages and versions should be installed so no environment can get tempted to fetch newer versions than expected.
* **Update:** as of npm 5, dependencies are locked automatically using .shrinkwrap. Yarn, an emerging package manager, also locks down dependencies by default.
<br/><br/>
### Code example: .npmrc file that instructs npm to use exact versions
```npmrc
// save this as .npmrc file on the project directory
save-exact:true
```
<br/><br/>
### Code example: shrinkwrap.json file that distills the exact dependency tree
```json
{
"name": "A",
"dependencies": {
"B": {
"version": "0.0.1",
"dependencies": {
"C": {
"version": "0.1.0"
}
}
}
}
}
```
<br/><br/>
### Code example: npm 5 dependencies lock file package.json
```json
{
"name": "package-name",
"version": "1.0.0",
"lockfileVersion": 1,
"dependencies": {
"cacache": {
"version": "9.2.6",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz",
"integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg=="
},
"duplexify": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
"integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
"dependencies": {
"end-of-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
"integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4="
}
}
}
}
}
```

View File

@ -0,0 +1,25 @@
# Measure and guard the memory usage
<br/><br/>
### One Paragraph Explainer
In a perfect world, a web developer shouldnt deal with memory leaks. In reality, memory issues are a known Nodes gotcha one must be aware of. Above all, memory usage must be monitored constantly. In the development and small production sites, you may gauge manually using Linux commands or npm tools and libraries like node-inspector and memwatch. The main drawback of this manual activities is that they require a human being actively monitoring for serious production sites, its absolutely vital to use robust monitoring tools e.g. (AWS CloudWatch, DataDog or any similar proactive system) that alerts when a leak happens. There are also few development guidelines to prevent leaks: avoid storing data on the global level, use streams for data with dynamic size, limit variables scope using let and const.
<br/><br/>
### What Other Bloggers Say
* From the blog [Dyntrace](http://apmblog.dynatrace.com/):
> ... ”As we already learned, in Node.js JavaScript is compiled to native code by V8. The resulting native data structures dont have much to do with their original representation and are solely managed by V8. This means that we cannot actively allocate or deallocate memory in JavaScript. V8 uses a well-known mechanism called garbage collection to address this problem.”
* From the blog [Dyntrace](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load):
> ... “Although this example leads to obvious results the process is always the same:
Create heap dumps with some time and a fair amount of memory allocation in between
Compare a few dumps to find out whats growing”
* From the blog [Dyntrace](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load):
> ... “fault, Node.js will try to use about 1.5GBs of memory, which has to be capped when running on systems with less memory. This is the expected behavior as garbage collection is a very costly operation.
The solution for it was adding an extra parameter to the Node.js process:
node max_old_space_size=400 server.js production ”
“Why is garbage collection expensive? The V8 JavaScript engine employs a stop-the-world garbage collector mechanism. In practice, it means that the program stops execution while garbage collection is in progress.”

View File

@ -0,0 +1,39 @@
# Monitoring!
<br/><br/>
### One Paragraph Explainer
At the very basic level, monitoring means you can *easily* identify when bad things happen at production. For example, by getting notified by email or Slack. The challenge is to choose the right set of tools that will satisfy your requirements without breaking your bank. May I suggest, start with defining the core set of metrics that must be watched to ensure a healthy state CPU, server RAM, Node process RAM (less than 1.4GB), the number of errors in the last minute, number of process restarts, average response time. Then go over some advanced features you might fancy and add to your wish list. Some examples of a luxury monitoring feature: DB profiling, cross-service measuring (i.e. measure business transaction), front-end integration, expose raw data to custom BI clients, Slack notifications and many others.
Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog, NewRelic and alike. Unfortunately, achieving even the basics is not a walk in the park as some metrics are hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. [AWS CloudWatch](https://aws.amazon.com/cloudwatch/), [Google StackDriver](https://cloud.google.com/stackdriver/)) will tell you immediately about the hardware metrics but not about the internal app behavior. On the other end, Log-based solutions such as ElasticSearch lack the hardware view by default. The solution is to augment your choice with missing metrics, for example, a popular choice is sending application logs to [Elastic stack](https://www.elastic.co/products) and configure some additional agent (e.g. [Beat](https://www.elastic.co/products)) to share hardware-related information to get the full picture.
<br/><br/>
### Monitoring example: AWS cloudwatch default dashboard. Hard to extract in-app metrics
![AWS cloudwatch default dashboard. Hard to extract in-app metrics](/assets/images/monitoring1.png)
<br/><br/>
### Monitoring example: StackDriver default dashboard. Hard to extract in-app metrics
![StackDriver default dashboard. Hard to extract in-app metrics](/assets/images/monitoring2.jpg)
<br/><br/>
### Monitoring example: Grafana as the UI layer that visualizes raw data
![Grafana as the UI layer that visualizes raw data](/assets/images/monitoring3.png)
<br/><br/>
### What Other Bloggers Say
From the blog [Rising Stack](http://mubaloo.com/best-practices-deploying-node-js-applications/):
> …We recommend you to watch these signals for all of your services:
> Error Rate: Because errors are user facing and immediately affect your customers.
> Response time: Because the latency directly affects your customers and business.
> Throughput: The traffic helps you to understand the context of increased error rates and the latency too.
> Saturation: It tells how “full” your service is. If the CPU usage is 90%, can your system handle more traffic? …

View File

@ -0,0 +1,16 @@
# Make your code production-ready
<br/><br/>
### One Paragraph Explainer
Following is a list of development tips that greatly affect the production maintenance and stability:
* The twelve-factor guide Get familiar with the [Twelve factors](https://12factor.net/) guide
* Be stateless Save no data locally on a specific web server (see separate bullet Be Stateless)
* Cache Utilize cache heavily, yet never fail because of cache mismatch
* Test memory gauge memory usage and leaks as part your development flow, tools such as memwatch can greatly facilitate this task
* Name functions Minimize the usage of anonymous functions (i.e. inline callback) as a typical memory profiler will provide memory usage per method name
* Use CI tools Use CI tool to detect failures before sending to production. For example, use ESLint to detect reference errors and undefined variables. Use trace-sync-io to identify code that uses synchronous APIs (instead of the async version)
* Log wisely Include in each log statement contextual information, hopefully in JSON format so log aggregators tools such as Elastic can search upon those properties (see separate bullet Increase visibility using smart logs). Also, include transaction-id that identifies each request and allows to correlate lines that describe the same transaction (see separate bullet Include Transaction-ID)
* Error management Error handling is the Achilles heel of Node.js production sites many Node processes are crashing because of minor errors while others hang on alive in a faulty state instead of crashing. Setting your error handling strategy is absolutely critical, read here my [error handling best practices](http://goldbergyoni.com/checklist-best-practices-of-node-js-error-handling/)

View File

@ -0,0 +1,32 @@
# Set NODE_ENV = production
<br/><br/>
### One Paragraph Explainer
Process environment variables is a set of key-value pairs made available to any running program, usually for configuration purposes. Though any variables can be used, Node encourages the convention of using a variable called NODE_ENV to flag whether were in production right now. This determination allows components to provide better diagnostics during development, for example by disabling caching or emitting verbose log statements. Any modern deployment tool Chef, Puppet, CloudFormation, others support setting environment variables during deployment
<br/><br/>
### Code example: Setting and reading the NODE_ENV environment variable
```javascript
// Setting environment variables in bash before starting the node process
$ NODE_ENV=development
$ node
// Reading the environment variable using code
if (process.env.NODE_ENV === production)
useCaching = true;
```
<br/><br/>
### What Other Bloggers Say
From the blog [dynatrace](https://www.dynatrace.com/blog/the-drastic-effects-of-omitting-node_env-in-your-express-js-applications/):
> ...In Node.js there is a convention to use a variable called NODE_ENV to set the current mode. We see that it, in fact, reads NODE_ENV and defaults to development if it isnt set. We clearly see that by setting NODE_ENV to production the number of requests Node.js can handle jumps by around two-thirds while the CPU usage even drops slightly. *Let me emphasize this: Setting NODE_ENV to production makes your application 3 times faster!*
![NODE_ENV=production](/assets/images/setnodeenv1.png "NODE_ENV=production")
<br/><br/>

View File

@ -0,0 +1,40 @@
# Make your app transparent using smart logs
<br/><br/>
### One Paragraph Explainer
Since you print out log statements anyway and you're obviously in a need of some interface that wraps up production information where you can trace errors and core metrics (e.g. how many errors happen every hour and which is your slowest API end-point) why not invest some moderate effort in a robust logging framework that will tick all boxes? Achieving that requires a thoughtful decision on three steps:
**1. smart logging** at the bare minimum you need to use a reputable logging library like [Winston](https://github.com/winstonjs/winston), [Bunyan](https://github.com/trentm/node-bunyan) and write meaningful information at each transaction start and end. Consider to also format log statements as JSON and provide all the contextual properties (e.g. user id, operation type, etc) so that the operations team can act on those fields. Include also a unique transaction ID at each log line, for more information refer to the bullet below “Write transaction-id to log”. One last point to consider is also including an agent that logs the system resource like memory and CPU like Elastic Beat.
**2. smart aggregation** once you have comprehensive information on your servers file system, its time to periodically push these to a system that aggregates, facilities and visualizes this data. The Elastic stack, for example, is a popular and free choice that offers all the components to aggregate and visualize data. Many commercial products provide similar functionality only they greatly cut down the setup time and require no hosting.
**3. smart visualization** now the information is aggregated and searchable, one can be satisfied only with the power of easily searching the logs but this can go much further without coding or spending much effort. We can now show important operational metrics like error rate, average CPU throughout the day, how many new users opted-in in the last hour and any other metric that helps to govern and improve our app
<br/><br/>
### Visualization Example: Kibana (part of the Elastic stack) facilitates advanced searching on log content
![Kibana facilitates advanced searching on log content](/assets/images/smartlogging1.png "Kibana facilitates advanced searching on log content")
<br/><br/>
### Visualization Example: Kibana (part of the Elastic stack) visualizes data based on logs
![Kibana visualizes data based on logs](/assets/images/smartlogging2.jpg "Kibana visualizes data based on logs")
<br/><br/>
### Blog Quote: Logger Requirements
From the blog [Strong Loop](https://strongloop.com/strongblog/compare-node-js-logging-winston-bunyan/):
> Lets identify a few requirements (for a logger):
> 1. Timestamp each log line. This one is pretty self-explanatory you should be able to tell when each log entry occurred.
> 2. Logging format should be easily digestible by humans as well as machines.
> 3. Allows for multiple configurable destination streams. For example, you might be writing trace logs to one file but when an error is encountered, write to the same file, then into error file and send an email at the same time…
<br/><br/>
<br/><br/>

View File

@ -0,0 +1,26 @@
# Utilize all CPU cores
<br/><br/>
### One Paragraph Explainer
It might not come as a surprise that in its basic form, Node runs over a single thread=single process=single CPU. Paying for beefy hardware with 4 or 8 CPU and utilizing only one sounds crazy, right? The quickest solution which fits medium sized apps is using Nodes Cluster module which in 10 lines of code spawns a process for each logical core and route requests between the processes in a round-robin style. Even better, use PM2 which sugarcoats the clustering module with a simple interface and cool monitoring UI. While this solution works well for traditional applications, it might fall short for applications that require top-notch performance and robust DevOps flow. For those advanced use cases, consider replicating the NODE process using custom deployment script and balancing using a specialized tool such as nginx or use a container engine such as AWS ECS or Kubernetees that have advanced features for deployment and replication of processes.
<br/><br/>
### Comparison: Balancing using Nodes cluster vs nginx
![Balancing using Nodes cluster vs nginx](/assets/images/utilizecpucores1.png "Balancing using Nodes cluster vs nginx")
<br/><br/>
### What Other Bloggers Say
* From the [Node.js documentation](https://nodejs.org/api/cluster.html#cluster_how_it_works):
> ... The second approach, Node clusters, should, in theory, give the best performance. In practice, however, distribution tends to be very unbalanced due to operating system scheduler vagaries. Loads have been observed where over 70% of all connections ended up in just two processes, out of a total of eight ...
* From the blog [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-two-performance-and-reliability/):
> ... Clustering is made possible with Nodes cluster module. This enables a master process to spawn worker processes and distribute incoming connections among the workers. However, rather than using this module directly, its far better to use one of the many tools out there that do it for you automatically; for example node-pm or cluster-service ...
* From the Medium post [Node.js process load balance performance: comparing cluster module, iptables, and Nginx](https://medium.com/@fermads/node-js-process-load-balancing-comparing-cluster-iptables-and-nginx-6746aaf38272)
> ... Node cluster is simple to implement and configure, things are kept inside Nodes realm without depending on other software. Just remember your master process will work almost as much as your worker processes and with a little less request rate than the other solutions ...

View File

@ -1,36 +1,37 @@
# 组件式构建你的解决方案
# Structure your solution by components
<br/><br/>
### One Paragraph Explainer
### 一段解释
对于中等规模的应用程序及以上,一个代码库是非常糟糕的 - 一个包含很多依赖的大型软件很难理解,往往导致代码混乱。即使是那些擅长解决负责问题和 "模块化" 的聪明架构师 - 在设计上花费了很大的脑力, 每一个变化都需要仔细评估对其他依赖对象的影响。最终的解决方案是开发小型软件将整个堆栈划分为独立的组件这些组件不与其他组件共享文件每个组件由很少的文件构成例如API、服务、数据访问、测试等因此很容易理解它。有些人可能称之为 "microservices" 架构 - 重要的是要理解 microservices 不是一个你必须遵循的规范,而是一套原则。您可以将许多原则引入到成熟的 microservices 体系结构中, 或者只采用少数几个。只要您保持软件的复杂性低, 两者都是好的。最起码应该做的是在组件之间创建一个基本边界, 为每个业务组件在项目根目录中分配一个文件夹, 并使其自包含-其他组件只能通过其公共接口或 API 使用其功能。这是保持您的组件简单的基础,在未来, 一旦您的应用程序增长,避免依赖性地狱,为全面的 microservices 架构铺平了道路.
For medium sized apps and above, monoliths are really bad - having one big software with many dependencies is just hard to reason about and often leads to spaghetti code. Even smart architects — those who are skilled enough to tame the beast and 'modularize' it — spend great mental effort on design, and each change requires carefully evaluating the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc.) so that it's very easy to reason about it. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows.
<br/><br/>
### Blog Quote: "Scaling requires scaling of the entire application"
### 博客引用: "伸缩需要对整个应用程序进行伸缩设计"
摘自博客 MartinFowler.com
> 单个应用程序可以成功, 但越来越多的人对它们感到失望 - 尤其是随着更多的应用程序被部署到云中。更改周期被捆绑在一起 - 对应用程序的一小部分进行更改, 需要重建和部署整个整体。随着时间的推移, 通常很难保持一个良好的模块化结构, 这使得更改哪怕只会影响该模块中的一个模块变得更加困难。伸缩设计需要扩展整个应用程序, 而不是它的部分,这往往需要更多资源。
<br/><br/>
### 博客引用: "那么, 你的应用程序的架构声明了什么?"
摘自博客 [uncle-bob](https://8thlight.com/blog/uncle-bob/2011/09/30/Screaming-Architecture.html)
> ...如果你正在寻找一个图书馆的建筑架构, 你可能会看到一个盛大的入口, 一个 check-in-out 的文员, 阅读区, 小会议室, 画廊, 画廊后面容纳了装载所有图书馆书籍的书架。建筑会声明: 图书馆.<br/>
那么, 应用程序的体系架构会声明什么呢? 当您查看顶级目录结构和最高级别包中的源文件时; 他们声明: 医疗保健系统, 或会计系统, 或库存管理系统? 或者他们声明: Rails, 或Spring/Hibernate, 或 ASP.
From the blog MartinFowler.com
> Monolithic applications can be successful, but increasingly people are feeling frustrations with them - especially as more applications are being deployed to the cloud. Change cycles are tied together - a change made to a small part of the application requires the entire monolith to be rebuilt and deployed. Over time it's often hard to keep a good modular structure, making it harder to keep changes that ought to only affect one module within that module. Scaling requires scaling of the entire application rather than parts of it that require greater resource.
<br/><br/>
### 推荐: 通过独立组件构建解决方案
### Blog Quote: "So what does the architecture of your application scream?"
From the blog [uncle-bob](https://8thlight.com/blog/uncle-bob/2011/09/30/Screaming-Architecture.html)
> ...if you were looking at the architecture of a library, youd likely see a grand entrance, an area for check-in-out clerks, reading areas, small conference rooms, and gallery after gallery capable of holding bookshelves for all the books in the library. That architecture would scream: Library.<br/>
So what does the architecture of your application scream? When you look at the top level directory structure, and the source files in the highest level package; do they scream: Health Care System, or Accounting System, or Inventory Management System? Or do they scream: Rails, or Spring/Hibernate, or ASP?.
<br/><br/>
### Good: Structure your solution by self-contained components
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")
<br/><br/>
<br/><br/>
### Bad: Group your files by technical role
### 避免: 按技术角色对文件进行分组
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles")

View File

@ -0,0 +1,41 @@
# Use environment aware, secure and hierarchical config
<br/><br/>
### One Paragraph Explainer
When dealing with configuration data, many things can just annoy and slow down:
1. setting all the keys using process environment variables becomes very tedious when in need to inject 100 keys (instead of just committing those in a config file), however when dealing with files only the DevOps admins cannot alter the behavior without changing the code. A reliable config solution must combine both configuration files + overrides from the process variables
2. when specifying all keys in a flat JSON, it becomes frustrating to find and modify entries when the list grows bigger. A hierarchical JSON file that is grouped into sections can overcome this issue + few config libraries allow to store the configuration in multiple files and take care to union all at runtime. See example below
3. storing sensitive information like DB password is obviously not recommended but no quick and handy solution exists for this challenge. Some configuration libraries allow to encrypt files, others encrypt those entries during GIT commits or simply don't store real values for those entries and specify the actual value during deployment via environment variables.
4. some advanced configuration scenarios demand to inject configuration values via command line (vargs) or sync configuration info via a centralized cache like Redis so multiple servers will use the same configuration data.
Some configuration libraries can provide most of these features for free, have a look at npm libraries like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) which tick many of these requirements.
<br/><br/>
### Code Example hierarchical config helps to find entries and maintain huge config files
```js
{
// Customer module configs
"Customer": {
"dbConfig": {
"host": "localhost",
"port": 5984,
"dbName": "customers"
},
"credit": {
"initialLimit": 100,
// Set low for development
"initialDays": 1
}
}
}
```
<br/><br/>

View File

@ -0,0 +1,59 @@
# Separate Express 'app' and 'server'
<br/><br/>
### One Paragraph Explainer
The latest Express generator comes with a great practice that is worth to keep - the API declaration is separated from the network related configuration (port, protocol, etc). This allows testing the API in-process, without performing network calls, with all the benefits that it brings to the table: fast testing execution and getting coverage metrics of the code. It also allows deploying the same API under flexible and different network conditions. Bonus: better separation of concerns and cleaner code
<br/><br/>
### Code example: API declaration, should reside in app.js
```javascript
var app = express();
app.use(bodyParser.json());
app.use("/api/events", events.API);
app.use("/api/forms", forms);
```
<br/><br/>
### Code example: Server network declaration, should reside in /bin/www
```javascript
var app = require('../app');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
```
### Example: test your API in-process using supertest (popular testing package)
```javascript
const app = express();
app.get('/user', function(req, res) {
res.status(200).json({ name: 'tobi' });
});
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if (err) throw err;
});
````

View File

@ -0,0 +1,13 @@
# Wrap common utilities as npm packages
<br/><br/>
### One Paragraph Explainer
Once you start growing and have different components on different servers which consumes similar utilities, you should start managing the dependencies - how can you keep 1 copy of your utility code and let multiple consumer components use and deploy it? well, there is a tool for that, it's called npm... Start by wrapping 3rd party utility packages with your own code to make it easily replaceable in the future and publish your own code as private npm package. Now, all your code base can import that code and benefit free dependency management tool. It's possible to publish npm packages for your own private use without sharing it publicly using [private modules](https://docs.npmjs.com/private-modules/intro), [private registry](https://npme.npmjs.com/docs/tutorials/npm-enterprise-with-nexus.html) or [local npm packages](https://medium.com/@arnaudrinquin/build-modular-application-with-npm-local-modules-dfc5ff047bcc)
<br/><br/>
### Sharing your own common utilities across environments and components
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components")

View File

@ -0,0 +1,51 @@
# Carefully choose your CI platform
<br/><br/>
### One Paragraph Explainer
The CI world used to be the flexibility of [Jenkins](https://jenkins.io/) vs the simplicity of SaaS vendors. The game is now changing as SaaS providers like [CircleCI](https://circleci.com/) and [Travis](https://travis-ci.org/) offer robust solutions including Docker containers with minimum setup time while Jenkins tries to compete on 'simplicity' segment as well. Though one can setup rich CI solution in the cloud, should it required to control the finest details Jenkins is still the platform of choice. The choice eventually boils down to which extent the CI process should be customized: free and setup free cloud vendors allow to run custom shell commands, custom docker images, adjust the workflow, run matrix builds and other rich features. However, if controlling the infrastructure or programming the CI logic using a formal programming language like Java is desired - Jenkins might still be the choice. Otherwise, consider opting for the simple and setup free cloud option
<br/><br/>
### Code Example a typical cloud CI configuration. Single .yml file and that's it
```javascript
version: 2
jobs:
build:
docker:
- image: circleci/node:4.8.2
- image: mongo:3.4.4
steps:
- checkout
- run:
name: Install npm wee
command: npm install
test:
docker:
- image: circleci/node:4.8.2
- image: mongo:3.4.4
steps:
- checkout
- run:
name: Test
command: npm test
- run:
name: Generate code coverage
command: './node_modules/.bin/nyc report --reporter=text-lcov'
- store_artifacts:
path: coverage
prefix: coverage
```
### Circle CI - almost zero setup cloud CI
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/circleci.png "API error handling")
### Jenkins - sophisticated and robust CI
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling")
<br/><br/>