In this post, I’d like to teach you some of the Heroku best practices we use at RisingStack for going to production with Node.jsNode.js is an asynchronous event-driven JavaScript runtime and is the most effective when building scalable network applications. Node.js is free of locks, so there's no chance to dead-lock any process., and give you a general checklist as well.
You are going to learn how to deploy applications to production, how to do proper logging and monitoring, and how to debug effectively.
These best practices will save you from false alarms waking you up in the nights as well as provide a consistent user experience for your users.
Step #1: Run Your Tests Automatically
All applications – not just Node.js – must have a proper test suite. The test suite functions as a safeguard, so you won’t accidentally change the functionality of a given module, or worse, the whole application.
All tests in Node.js should run using the npm test
command, so you should define your test commands in your package.json
file’s scripts
section.
{
"scripts": {
"test": "NODE_ENV=test mocha --require co-mocha test/setup.js '**/*.spec.js'"
}
}
<p class="click-to-tweet-button">
<a href="https://twitter.com/share?text=%22We%20recommend%20putting%20your%20test%20files%20next%20to%20the%20implementation%2C%20and%20name%20them%20%60.spec.js%60.%22%20via%20%40RisingStack;url=https://community.risingstack.com/using-heroku-node-js-production-ready-application-checklist" target="_blank" c>Click To Tweet</a>
</p>
Step #2: Do Automatic Deployments
We see lots of manual steps involved in deployment, even in bigger systems. This approach is very error-prone – in case someone forgets something, you will have a bad time. Because of this, you should never do deployment manually.
Instead of that, you can automate the whole process with great tools like Codeship or CircleCI. These tools should run your tests, and if everything is green, it should deploy your software. In CircleCI, we usually set up our tests to run these commands:
test:
pre:
- npm install
override:
- npm run lint
- npm test
Once all the tests are passed, the CI has to deploy our application. But where should it deploy it to?
At RisingStack, we usually have two environments, one called Staging, and one called Production. The CI ships the application to the Staging environment. There is a manual step involved to move the application from Staging to Production. On Heroku, you have the Pipeline feature for this.
On the UI with the Promote to production...
button, you can simply push your Staging application to Production. These applications share the same codebase but can have different environment variables so that you can connect them to your staging databases.
To read more about how you should structure your applications on Heroku, I’d recommend reading the 12-factor application principles.
Step #3: Set Up Proper Logging
Logging in production is crucial. Logging in Node.js enables you to:
- have a better understanding of how your applications work,
- discover what errors you have,
- find out if your services are running correctly.
Proper logging should always have a
- timestamp,
- a format that’s easily understandable for humans and machines as well,
- a log destination, preferably the standard output,
- support for log levels, so you can dynamically modify what to log.
At RisingStack, we mostly use winston. Winston is a multi-transport asyncAsynchrony, in software programming, refers to events that occur outside of the primary program flow and methods for dealing with them. External events such as signals or activities prompted by a program that occur at the same time as program execution without causing the program to block and wait for results are examples of this category. Asynchronous input/output is an... logging library for Node.js.
You can add winston to your project by installing it:
npm install winston --save
To create your first log line, you can run something like this:
const winston = require('winston')
winston.log('info', 'Hello log files!', {
someKey: 'some-value'
})
The output of the snippet above will be:
info: Hello log files! someKey=some-value
You might notice, that the first argument to the winston.log
was info
– this is where you can specify the log level of a given log record. You can modify the current log level you use, with assigning the new level to winston.level
, like winston.level = 'debug'
. By default, winston supports error
, warn
, info
, verbose
, debug
, and silly
levels.
You can set the
winston.level
from an environment variable, like =winston.level = process.env.LOG_LEVEL
, so whenever your application restarts, the new levels will be applied.
If you’re looking for great log providers on Heroku, you can start using Logentries, Papertrail or Logz to store and search your logs.
Step #4: Set Up Alerts in Production
Both Logging and Monitoring is a must for production systems – as you have logging in place already, let’s take on why you need monitoring and how you can set yours up!
You have an obligation to continuously detect bottlenecks and figure out what slows your product down.
An even greater issue is to handle and preempt downtimes. You must be notified as soon as they happen, preferably before your customers start to complain. Based on these needs, proper monitoring should give you at least the following features and insights into your application’s behavior:
- performance dashboard, to provide a quick overview of the state of your application,
- monitoring network connections,
- real-time alerting,
- code-level insights.
You can install Trace as Heroku addon to solve this task:
Once you do that, you have to follow the onboarding steps – this shouldn’t take more than a couple of minutes.
Step #5: Profile your Production Systems
Profiling on the code level is essential to understand how much time does your functions take to run in the actual production environment. Luckily, Trace covers this area as well.
All you have to do is to head over to the CPU Profiles tab on the Profiling page. Here you can request and download a profile which you can load into the Chrome DevTool as well.
Step #6: Find the Memory Leaks
Go to the Profiler page in Trace and request a new memory heap dump, then wait 5 minutes and request another. Download them and open them on Chrome DevTool’s Profiles page. Select the second one (the most recent one), and click Comparison.
Okay, but what does this graph mean?
When you search a memory leak, you have to look for the #Delta column. Click on it, and you will see the number of additional elements in the second memory dump (compared to the first one).
On the bottom of the picture, you can see what these elements were, and you can start figuring out what caused the leak.
Heroku & Node.js = <3
Running a production app on Heroku is quite easy if you follow these best practices. Of course, there’s much more to monitoring your applications performance on Heroku; we just got the basics right this time.
If you’d like to get a little bit better with measuring and optimizing your Node apps performance, I recommend to go through these articles:
- Node.js Monitoring Done Right
- [Hunting a Ghost – Finding a Memory Leak in Node.js](Hunting a Ghost – Finding a Memory Leak in Node.js)
- Introducing Distributed Tracing for Microservices Monitoring
This article is written by Gergely Nemeth. The author’s bio:
“Co-founder of RisingStack”