wearehive / project-guidelines
- четверг, 6 июля 2017 г. в 03:13:39
A set of best practices for JavaScript projects
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.
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:
git checkout -b <branchname>
git add
git commit -m "description of changes"
git checkout develop
git pull
git checkout <branchname>
git -i rebase develop
git add <file1> <file2> ...
git rebase --continue
git push
There are a set of rules to keep in mind:
develop
develop
or master
branch.develop
and do a interactive rebase before pushing your feature and making a PRdevelop
and master
branch (How to in Github and Bitbucket).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):
README.md
, Feel free to add uncovered sections.README.md
files.README.md
updated as project evolves.development
, test
and production
environments..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.joi
to validate provided values:engines
in package.json
to specify the version of node your project works on.nvm
and create a .nvmrc
in your project root. Don't forget to mention in documentationpreinstall
script that checks node and npm versionsBefore using a package check its Github open issues, daily downloads and number of contributors as well as the date package last updated.
npm ls --depth=0
(documentation).depcheck
(documentation).npm-stat
(documentation).npm view async
(documentation).npm outdated
(documentation).package-lock.json
on npm 5 or higher—save --save-exact
when installing a new dependency and create npm-shrinkwrap.json
before publishing.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.*.test.js
or *.spec.js
naming convention, like module_name.spec.js
develop
.Organize your files around product features / pages / components, not Roles:
// BAD
.
├── controllers
| ├── product.js
| └── user.js
├── models
| ├── product.js
| └── user.js
// GOOD
.
├── product
| ├── index.js
| ├── product.js
| └── product.test.js
├── user
| ├── index.js
| ├── user.js
| └── user.test.js
Place your test files next to the 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 environmental 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 too. 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 would match the name of the default export of index.js
.
.eslintignore
to exclude file or folders from code style check.eslint
disable comments before making a Pull Request.//todo:
comments to remind yourself and others about an unfinished job.Follow resource-oriented design. This has three main factors: resources, collection, and URLs.
/users
a collection of users (plural nouns)./users/id
a resource with information about a specific user.GET /translate?text=Hallo
camelCase
to maintain the consistency.table_name
for a resource name as well. Same with resource properties, they shouldn't be the same as your column names.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:
Sub resources are used to link one resource with another, so use sub resources to represent the relation.
An API is supposed to be an interface for developers and this is a natural way to make resources explorable.
If there is a relation between resources like employee to a company, use id
in the URL:
/schools/2/students
Should get the list of all students from school 2/schools/2/students/31
Should get the details of student 31, which belongs to school 2/schools/2/students/31
Should delete student 31, which belongs to school 2/schools/2/students/31
Should update info of student 31, Use PUT on resource-URL only, not collection/schools
Should create a new school and return the details of the new school created. Use POST on collection-URLsWhen your APIs are public 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
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"
}
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"
}
]
}
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.
200
HTTP response representing success for GET, PUT or POST.201
Created This status code should be returned whenever the 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 is 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.400
Bad Request indicates that the request by the client was not processed, as the server could not understand what the client is asking for.401
Unauthorised indicates that the client is not allowed to access resources, and should re-request with the required credentials.403
Forbidden indicates that the request is valid and the client is authenticated, but the client is not allowed access the page or resource for any reason.404
Not Found indicates that the requested resource is not available now.406
Not Acceptable response. A lack of Content-Type header or an unexpected Content-Type header should result in the server rejecting the content410
Gone indicates that the requested resource is no longer available which has been intentionally moved.500
Internal Server Error indicates that the request is valid, but the server is totally confused and the server is asked to serve 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.GET /student?fields=id,name,age,class
To secure your web API authentication, all authentications should use SSL. OAuth2 requires the authorization server and access token credentials to use TLS. Switching between HTTP and HTTPS introduces security weaknesses and best practice is to use TLS by default for all communication. Throw an error for non-secure access to API URLs.
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.
It's difficult to perform most attacks if the allowed values are limited.
Validate required fields, field types (e.g. string, integer, boolean, etc), and format requirements. Return 400 Bad Request with details about any errors from bad or missing data.
Escape parameters that will become part of the SQL statement to protect from SQL injection attacks
As also mentioned before, don't expose your database scheme when naming your resources and defining your responses
Attackers can tamper with any part of an HTTP request, including the URL, query string,
The server should never assume the Content-Type. A lack of Content-Type header or an unexpected Content-Type header should result in the server rejecting the content with a 406
Not Acceptable response.
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.
API Reference
section in README.md template for API.For each endpoint explain:
Required: id=[integer]
Optional: photo_id=[alphanumeric]
Code: 200
Content: { id : 12 }
"Code": 403
"message" : "Authentication failed",
"description" : "Invalid username or password"
There are lots of open source tools for good documentation such as API Blueprint, Swagger , ENUNCIATE and Miredot, which can be used.
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