While developing a new project is like rolling on a green field for you, maintaining it is a potential dark twisted nightmare for someone else. Here’s a list of guidelines we’ve found, written and gathered that (we think) works really well with most JavaScript projects here at hive. If you want to share a best practice, or think one of these guidelines should be removed, feel free to share it with us.

  • Git
  • Documentation
  • Environments
  • Dependencies
  • Testing
  • Structure and Naming
  • Code style
  • Logging
  • API Design
  • Licensing

1. Git

1.1 the Git Workflow

We use Feature-branch-workflow with Interactive Rebasing and some elements of Gitflow (naming and having a develop branch). The main steps are as follow:

  • Checkout a new feature/bug-fix branch
    git checkout -b <branchname>Copy the code

  • Make Changes
    git add
    git commit -m "<description of changes>"Copy the code

  • Sync with remote to get changes you’ve missed
    git checkout develop
    git pullCopy the code

  • Update your feature branch with latest changes from develop by interactive rebase (Here is why)
    git checkout <branchname>
    git rebase -i developCopy the code

  • If you don’t have conflict skip this step. If you have conflicts, resolve them and continue rebase
    git add <file1 > <file2 >. git rebase --continueCopy the code

  • Push your branch. Rebase will change history, so you’ll have to use -f to force changes into the remote branch. If someone else is working on your branch, use the less destructive --force-with-lease (Here is why).
    git push -fCopy the code

  • Make a Pull Request.
  • Pull request will be accepted, merged and close by reviewer.
  • Remove your local feature branch if you’re done.

1.2 Some Git Rules

There are a set of rules to keep in mind:

  • Perform work in a feature branch.
  • Make pull requests to develop
  • Never push into develop or master branch.
  • Update your develop and do a interactive rebase before pushing your feature and making a Pull Request
  • Resolve potential conflicts while rebasing and before making a Pull Request
  • Delete local and remote feature branches after merging.
  • Before making a Pull Request, make sure your feature branch builds successfully and passes all tests (including code style checks).
  • Use this .gitignore file.
  • Protect your develop and master branch (How to in Github and Bitbucket).

1.3 Writing good commit messages

Having a good guideline for creating commits and sticking to it makes working with Git and collaborating with others a lot easier. Here are some rules of thumb (source):

  • Separate the subject from the body with a blank line
  • Limit the subject line to 50 characters
  • Capitalize the subject line
  • Do not end the subject line with a period
  • Use imperative mood in the subject line
  • Wrap the body at 72 characters
  • Use the body to explain what and why as opposed to how

2. Documentation

  • Use this template for README.md, Feel free to add uncovered sections.
  • For projects with more than one repository, provide links to them in their respective README.md files.
  • Keep README.md updated as project evolves.
  • Comment your code. Try to make it as clear as possible what you are intending with each major section.
  • If there is an open discussion on github or stackoverflow about the code or approach you’re using, include the link in your comment,
  • Don’t use commenting as an excuse for a bad code. Keep your code clean.
  • Don’t use clean code as an excuse to not comment at all.
  • Comment even small sections of code if you think it’s not self explanatory.
  • Keep comments relevant as your code evolves.

3. Environments

  • Depending on project size, define separate development.test and production environments.
  • Load your deployment specific configurations from environment variables and never add them to the codebase as constants, look at this sample.
  • Your config should be correctly separated from the app internals as if the codebase could be made public at any moment. Use .env files to store your variables and add them to .gitignore to be excluded from your code base because of course, you want the environment to provide them. Instead commit a .env.example which serves as a guide for developers to know which environment variables the project needs. It is important to remember that this setup should only be used for development. For production you should still set your environment variables in the standard way.
  • It’s recommended to validate environment variables before your app starts. Look at this sample using joi to validate provided values.

3.1 Consistent dev environments:

  • Set engines in package.json to specify the version of node your project works on.
  • Additionally, use nvm and create a .nvmrc in your project root. Don’t forget to mention it in the documentation
  • You can also use a preinstall script that checks node and npm versions
  • Use Docker images provided it doesn’t make things more complicated
  • Use local modules instead of using globally installed modules

Before using a package, check its GitHub. Look for the number of open issues, daily downloads and number of contributors as well as the date the package was last updated.

4.1 Consistent dependencies:

  • Use package-lock.json on npm@5 or higher
  • For older versions of npm, use Save - save - exact when installing a new dependency and create npm-shrinkwrap.json before publishing.
  • Alternatively you can use Yarn and make sure to mention it in README.md. Your lock file and package.json should have the same versions after each dependency update.
  • Read more here: package-locks | npm Documentation

5. Testing

  • Have a test mode environment if needed.
  • Place your test files next to the tested modules using *.test.js or *.spec.js naming convention, like module_name.spec.js
  • Put your additional test files into a separate test folder to avoid confusion.
  • Write testable code, avoid side effects, extract side effects, write pure functions
  • Don’t write too many tests to check types, instead use a static type checker
  • Run tests locally before making any pull requests to develop.
  • Document your tests, with instructions.

6. Structure and Naming

  • Organize your files around product features / pages / components, not roles

Bad

. ├ ─ ─ controllers | ├ ─ ─ the product. The js | └ ─ ─ the user. The js ├ ─ ─ models | ├ ─ ─ the product. The js | └ ─ ─ user. JsCopy the code

Good

. ├ ─ ─ the product | ├ ─ ─ index. The js | ├ ─ ─ the product. The js | └ ─ ─ the product. The test. The js ├ ─ ─ the user | ├ ─ ─ index. The js | ├ ─ ─ the user. The js | └ ─ ─ user.test.jsCopy the code
  • Place your test files next to their implementation.
  • Put your additional test files to a separate test folder to avoid confusion.
  • Use a ./config folder. Values to be used in config files are provided by environment variables.
  • Put your scripts in a ./scripts folder. This includes bash and node scripts for database synchronisation, build and bundling and so on.
  • Place your build output in a ./build folder. Add build/ to .gitignore.
  • Use PascalCase' 'camelCase for filenames and directory names. Use PascalCase only for Components.
  • CheckBox/index.js should have the CheckBox component, as could CheckBox.js, but not CheckBox/CheckBox.js or checkbox/CheckBox.js which are redundant.
  • Ideally the directory name should match the name of the default export of index.js.

7. Code style

  • Use stage-1 and higher JavaScript (modern) syntax for new projects. For old project stay consistent with existing syntax unless you intend to modernise the project.
  • Include code style check before build process.
  • Use ESLint – Pluggable JavaScript linter to enforce code style.
  • We use Airbnb JavaScript Style Guide for JavaScript, Read more. Use the javascript style guide required by the project or your team.
  • We use Flow type style check rules for ESLint. when using FlowType.
  • Use .eslintignore to exclude file or folders from code style check.
  • Remove any of your eslint disable comments before making a Pull Request.
  • Always use //TODO: comments to remind yourself and others about an unfinished job.
  • Always comment and keep them relevant as code changes.
  • Remove commented block of code when possible.
  • Avoid js alerts in production.
  • Avoid irrelevant or funny comments, logs or naming (source code may get handed over to another company/client and they may not share the same banter).
  • Write testable code, avoid side effect, extract side effects, write pure functions.
  • Make your names search-able with meaningful distinctions avoid shortened names. For functions Use long, descriptive names. A function name should be a verb or a verb phrase, and it needs to communicate its intention.
  • Organize your functions in a file according to the step-down rule. Higher level functions should be on top and lower levels below. It makes it more natural to read the source code.

8. Logging

  • Avoid client-side console logs in production
  • Produce readable production logging. Ideally use logging libraries to be used in production mode (such as winston or node-bunyan).

9 API design

Follow resource-oriented design. This has three main factors: resources, collection, and URLs.

  • A resource has data, relationships to other resources, and methods that operate against it
  • A group of resources is called a collection.
  • URL identifies the online location of a resource.

9.1 API Naming

9.1.2 Naming fields

  • The request body or response type is JSON then please follow camelCase to maintain the consistency.
  • Expose Resources, not your database schema details. You don’t have to use your table_name for a resource name as well. Same with resource properties, they shouldn’t be the same as your column names.
  • Use individual names in your URL naming and don’t try to explain their functionality and Only explain the resources (elegantly).

9.2.1 Use HTTP methods

Only use nouns in your resource URLs, avoid endpoints like /addNewUser or /updateUser . Also avoid sending resource operations as a parameter. Instead explain the functionalities using HTTP methods:

  • GET Used to retrieve a representation of a resource.
  • POST Used to create new resources and sub-resources
  • PUT Used to update existing resources
  • PATCH Used to update existing resources. PATCH only updates the fields that were supplied, leaving the others alone
  • DELETE Used to delete existing resources

9.4 API Versioning

When your APIs are public for other third parties, upgrading the APIs with some breaking change would also lead to breaking the existing products or services using your APIs. Using versions in your URL can prevent that from happening: http://api.domain.com/v1/schools/3/students

9.5.1 Errors

Response messages must be self descriptive. A good error message response might look something like this:

{
"code": 1234."message" : "Something bad happened"."description" : "More details"
}Copy the code

or for validation errors:

{
  "code" : 2314."message" : "Validation Failed"."errors": [{"code" : 1233."field" : "email"."message" : "Invalid email"
    },
    {
       "code" : 1234."field" : "password"."message" : "No password provided"}}]Copy the code

Note: Keep security exception messages as generic as possible. For instance, Instead of saying ‘incorrect password’, you can reply back saying ‘invalid username or password’ so that we don’t unknowingly inform user that username was indeed correct and only password was incorrect.

The client and API worked (Success — 2xx Response code)
  • 200 OK This HTTP response represents success for GET.PUT or POST requests.
  • 201 Created This status code should be returned whenever a new instance is created. E.g on creating a new instance, using POST method, should always return 201 status code.
  • 204 No Content represents the request was successfully processed, but has not returned any content. DELETE can be a good example of this. If there is any error, then the response code would be not be of 2xx Success Category but around 4xx Client Error category.
The API pour Incorrectly (Server error — 5XX Response Code)
  • 500 Internal Server Error indicates that the request is valid, but the server could not fulfill it due to some unexpected condition.
  • 503 Service Unavailable indicates that the server is down or unavailable to receive and process the request. Mostly if the server is undergoing maintenance or facing a temporary overload.

9.7 the API security

9.7.2 Rate limiting

If your API is public or have high number of users, any client may be able to call your API thousands of times per hour. You should consider implementing rate limit early on.

9.7.4 URL validations

Attackers can tamper with any part of an HTTP request, including the URL, query string,

9.7.6 JSON encoding

A key concern with JSON encoders is preventing arbitrary JavaScript remote code execution within the browser or node.js, on the server. Use a JSON serialiser to entered data to prevent the execution of user input on the browser/server.

9.8.1 API design tools

There are lots of open source tools for good documentation such as API Blueprint and Swagger.

10. Licensing

Make sure you use resources that you have the rights to use. If you use libraries, remember to look for MIT, Apache or BSD but if you modify them, then take a look into licence details. Copyrighted images and videos may cause legal problems.


Sources: RisingStack Engineering, Mozilla Developer Network, Heroku Dev Center, Airbnb/javascript Atlassian Git tutorials