Skip to content

Latest commit

 

History

History
189 lines (138 loc) · 9.62 KB

STANDARDS.md

File metadata and controls

189 lines (138 loc) · 9.62 KB

Standards and Decisions

Disagree with something you saw in the project and want to understand why it's implemented this way? Just want to read up on my thoughts on JS/TS/PWA development? Either way, you're in the right place.

Preface

When or if you consider forking/cloning the project and use it as a base for any project, you can (obviously) disregard/alter any of the standards written here. Just be sure to keep this file up-to-date with your own coding standards. It's easy to lose your way after a while (or in a team).

Folder Structure

All configuration files live outside of any folders, in the root of the project to avoid pathing conflicts of any kind.

Any configuration that is environment- or IDE-specific is not to be included in the repository as it is the responsibility/choice of each developer. This includes the .editorconfig file as well - if linting tools are provided, then "any" editor can be configured to use them - no need to duplicate code/rules or restrain the developers. Any files that can be generated by the application are to be kept out of the repository as well.

All generated folders live in the root of the project for easy accessibility by internal or external tools. Examples of these types of folders are: coverage (for code coverage collection), node_modules (obviously) and public (the generated assets folder).

The config folder contains any and all files pertaining to building and configuring the environment for the application. This includes the build configuration for webpack (and its environments) and any environment-specific global runtime variables.

The src folder contains only (1) actual application code, (2) assets to be copied or used during the build process and (3) testing functions (helpers) to be used when testing the application. Inside the src folder, you'll find 3 folders that contain these types of code/information respectively. They are (1) app, (2) static and (3) testing.

Dependencies

All project dependencies are to be written as ~MAJOR.MINOR. Patch versioning is only acceptable where an incompatibility or breaking change exists between two patch versions.

Example 1

Project needs webpack, which is in version 3.9.1.

In the package.json file the dependency must be written as "webpack": "~3.9" so that any and all patches/bugfixes to 3.9 are applied to the project (3.9.1, 3.9.2, ...) but no (possible) breaking features (3.10.0) are added automatically.

Example 2

Project needs at least webpack 3.9.1 due to an impactful bugfix on that specific patch version.

In the package.json file the dependency must be written as "webpack": "~3.9 >=3.9.1" for the same reasons described above.

Dev and Prod Dependencies

In typical node applications, production packages are packages that are needed during runtime. All other dependencies are devDependencies. In applications that are completely pre-built and need no production packages (like in this repo), production packages are the packages needed to build the production version of an application. This makes things easier on automators like jenkins that build our production build but have no need to install, for example, linters. Additionally, this separation also makes a linter's job easier for detecting dev dependencies being bundled with the final production code.

Code

Structure

Any module or submodule is contained inside its own folder. No other information, asset, module or component is to be included in that folder.

Any component or module that is a direct child of the app folder (i.e. files inside the app folder) is a starting point for the application. In a PWA, there is only one.

Any component and module should have (when applicable): the module code (.js or .ts), the style (.css or .scss), the unit test (.spec.js or .spec.ts) and the export file (index.js or index.ts). This is intended for several reasons:

  • a component/module should be used as a blackbox
  • non-tested components/modules are easily spotted
  • adding/deleting a component/module is a clean and self-contained action

File names, and types should be done in kebab-case. File naming rule is the following: [name](.type?)(.spec?).[ext]. Examples:

  • example.component.ts - component named ExampleComponent
  • example.style.scss - style to be imported by ExampleComponent
  • example.component.spec.js - unit tests for ExampleComponent
  • my-module.js - service/module named MyModule
  • my-module.spec.ts - unit tests for MyModule

Note that index.js (or index.ts) is exempt from these rules as it is the default name and extension used on node for "importing a folder". This file should only import and export the component or module it pertains to.

Any cluster of similar modules should be grouped by folder inside app. Typical examples are:

  • containers - components that hold state information and control its flow
  • components - dumb components that receive information only
  • models - models/transformers/factories for objects
  • services - modules for calculations, API requests, ...
  • state or store - modules that contain the application's state

Note that these are only examples and that inside these folders, similar modules can and should also be grouped according to their relativeness or usefulness.

Style/Standards

Components and Modules

All imports follow these rules:

  • Order is: packages first, project modules next, and local modules last. There is always a blank line separating these 3 types of imports
  • Deconstructed import objects are written alphabetically. Types or interfaces (example: in typescript or when using JS PropTypes) are imported last when deconstructing.

No module or component is imported or exported through the default mechanism. Airbnb coding standards are intended for typical JS/Node packages but they do not take into account the volatility of code, features, requirements and dependencies that a frontend project has to deal with. For more information on the subject, see this issue and PR. If there needs to be a default import (for example due to a restriction of an external library), make sure there is also a matching named import in order to keep code consistency.

All other linting rules from airbnb-base must be followed as it is the chosen standard for this project.

Disabling rules on eslint or stylelint should only be done in cases where there is no other feasible option. When disabling a rule, disable it only for the next line of code with eslint-disable-next-line for 3 main reasons:

  • it is a failsafe to not have a rule disabled on the entire project due to forgetfulness or bad code reviews
  • never adds to the line char count
  • adding/removing them results in simpler diffs

Note that if 3 or more lines in a row require the disabling of a rule, use eslint-disable and eslint-enable.

Naming convention is as follows: class names, interfaces and typing in general are to be written in PascalCase, objects and singletons in camelCase and global variables in UPPER_SNAKE_CASE.

Unit Tests

Unit tests can generally be divided in 2 categories:

  • whitebox - for testing, for example, a service to fetch data from an API
  • blackbox - for testing, for example, a visual component of a web page

Regardless of what type of test you are performing, any and all external dependencies (parents and children) should be mocked - auto-mocked by jest if possible.

Service Unit Tests

Testing a service or module is done in a whitebox style, where every public function is tested independently and where external dependencies are mocked (sibling function mocking should be done on an as needed basis). This is done in order to verify that every function of the service/module behaves according to its specifications.

The first describe on a service test should be written as the name of the exported service; second-layer describes should represent function names and should be prefaced by a #; following describes should be written as if prefaced by the word when. When writing its, they should start with a present tense verb and should be read as if prefaced by the word it. (See src/app/services/helper/index.spec.js for an example)

Component Unit Tests

Testing a visual component is done is a blackbox style, where the only thing that matters is the result on the page (i.e. what is seen by the user). That said, the tests focus only on testing the output on the page. This is done because changing the library/framework, a dependency, parent/child component cannot majorly impact the unit tests.

The first describe on a component test should be written as the name of the exported component; following describes should be written as if prefaced by the word when. When writing its, they should start with a present tense verb and should be read as if prefaced by the word it. (See src/app/components/home/home.component.spec.ts for an example)

Coverage

Anything lower than 100% test coverage is not acceptable. If a component or module cannot be fully tested, then it should be refactored - testability is a measurement of code maintainability. Only in extreme cases should you use something like /* istanbul ignore next */.

Versioning

Versioning should be done in the semver way described here. When version is below v1, minor versions become breaking changes or new features, while patch versions become minor features or bugfixes.