Skip to content

daisugiland/javascript-style-guide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 

Repository files navigation

JavaScript style guide

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There’s more than one way to do it, but sometimes consistency is not a bad thing either. (TIMTOWTDIBSCINABTE)
Now is better than never.
Although later is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.

* Borrowed from Python zen with slight modifications. [+]

Table of contents

Naming conventions

Folders and files

  • kebab-case for folders.

    Covers if folder will be extracted to its own package some day. [+]

  • camelCase or PascalCase for files.

    Has problems on commiting case-sensitive filename changes with Git. [+]

    ✔️ Good

    .
    └── src/
        ├── product/
        │   ├── application/
        │   │   └── services/
        │   │       └── SearchProductsService.js
        │   ├── domain/
        │   │   └── services/
        │   │       └── SearchProductsBySKUService.js
        │   └── infrastructure/
        │       └── stores/
        │           └── ProductAPIStore.js
        └── app.js

    * Generated with https://tree.nathanfriend.io/.

  • A file that exports only one class, function, or constant should be named for that class, function or constant.

    ❌ Bad

    // file name: programstore.js
    
    export class ProgramStore {
      ...
    }

    ✔️ Good

    // file name: ProgramStore.js
    
    export class ProgramStore {
      ...
    }
  • Files whose exports multiple things, the file name should be kept short, meaningful and easily understandable to others.

  • Avoid index as a file name.

    It does not reflect the content of the file.

    The file can’t live outside of the folder, because breaks the chain between folder name and file name.

    NodeJS has a special treatment for index files, but other engines like Deno don’t.

    ❌ Bad

    .
    └── src/
        └── program-filters/
            ├── isNotOldFilter.js
            ├── hasMonthlyDownloadsFilter.js
            └── index.js

    ✔️ Good

    .
    └── src/
        └── program-filters/
            ├── isNotOldFilter.js
            ├── hasMonthlyDownloadsFilter.js
            └── programFilters.js
  • Avoid generic names for folders. Be precise.

    ❌ Bad

    .
    └── src/
        ├── utils/
        ├── config/
        ├── vendors/
        └── helpers/

    ✔️ Good

    .
    └── src/
        ├── program-guards/
        ├── auth/
        └── logger/

🔝 back to top

Pluralization

  • Pluralize only collections.

    ❌ Bad

    class ProgramsStore {
      constructor() {}
    }

    ✔️ Good

    class ProgramStore {
      constructor() {}
    }

    ✔️ Good

    customers.forEach((customer) => {
      ...
    });
  • Use singularPlural for a list of a single property. [+]

    ✔️ Good

    const programs = [{
      id: 1,
    }, {
      id: 2,
    }];
    
    const program = programs[0];
    const programId = 1;
    const programIds = [1, 2];
  • Use pluralOfSingular for a list of single item list.

    ✔️ Good

    const program = {
      topics: ['addons'],
    };
    
    const topicsOfProgram = ['addons'];
  • Use pluralOfPlural for a list of lists.

    ✔️ Good

    const programs = [{
      topics: ['addons'],
    }, {
      topics: ['skins'],
    }];
    
    const topicsOfPrograms = ['skins', 'addons'];

🔝 back to top

Variables

  • camelCase for variables.

    ❌ Bad

    const firstname = 'Benadryl';
    const first_name = 'Benadryl';
    const FIRSTNAME = 'Benadryl';
    const FIRST_NAME = 'Benadryl';

    ✔️ Good

    const firstName = 'Benadryl';

🔝 back to top

Acronyms

  • UPPERCASE for acronyms.

    Names are for readability, not to appease a computer algorithm. [+]

    ❌ Bad

    const { XMLHttpRequest } = require('...');
    
    const xmlHttpRequest = { ... };
    
    function requestIpAddress() {
      ...
    }
    
    function dbmxmlParse() {
      ...
    }

    ✔️ Good

    const { XMLHTTPRequest } = require('...');
    
    const xmlHTTPRequest = { ... };
    
    function requestIPAddress() {
      ...
    }
    
    function dbmXMLParse() {
      ...
    }

🔝 back to top

Abbreviations

  • camelCase for abbreviations.

    ❌ Bad

    const programID = 0; // Id[entifier].
    const isEXEFile = true; // Exe[cutable].
    const androidAPPName = 'Zoom'; // App[lication].

    ✔️ Good

    const programId = 0; // Id[entifier].
    const isExeFile = true; // Exe[cutable].
    const androidAppName = 'Zoom'; // App[lication].

🔝 back to top

Verbosity

  • Avoid use of abbreviations for naming—be verbose.

    ❌ Bad

    const accBalInSaving = 0;
    const dmgPerSec = 100;

    ✔️ Good

    const accountBalanceInSavings = 0;
    const damagePerSecond = 100;

🔝 back to top

HashMaps

  • keyToValue or valueByKey for HashMaps.

  • No rules for keys naming.

    ❌ Bad

    const mapStateCounty = {
      CA: 58,
    };
    
    const numberOfCountiesIn = {
      CA: 58,
    };
    
    const countyCountOf = {
      CA: 58,
    };

    ✔️ Good

    const stateToCountiesCount = {
      CA: 58,
    };
    
    const countiesCountByState = {
      CA: 58,
    };

🔝 back to top

Constants

  • UPPERCASE for constants.

    Constants are string or integer literals, used as aliases for “hard-coded” values.

    ✔️ Good

    const SECONDS = 60;
    const MINUTES = 60;
    const HOURS = 24;
    const DAY = SECONDS * MINUTES * HOURS;
    const DAYS_UNTIL_TOMORROW = 1;

🔝 back to top

Booleans

  • Use affirmative names.

  • Use is, are, have, has, can, should or any other prefix which indicates a yes or no as response. [+]

    ❌ Bad

    const isUsersLoggedIn = true;

    ✔️ Good

    const isEachUserLoggedIn = true;
  • Use affirmative names.

    ❌ Bad

    const isNotActive = true;
    
    if (!isNotActive) {
      ...
    }

    ✔️ Good

    const isActive = true;
    
    if (isActive) {
      ...
    }
  • Use convenient name when the boolean is optional with negative default value. [+]

    Avoid double negatives.

    Implicit default.

    ❌ Bad

    function createWidget(isEnabled = true) {
      ...
    }

    ✔️ Good

    function createWidget(isDisabled) {
      ...
    }

🔝 back to top

Contextualization

  • Do not add redundant context to variable names when the context is already provided by the containing object or class. [+]

    ❌ Bad

    const product = {
      productId: "XXX-XXX",
      productName: "book",
    };
    
    product.userId;

    ✔️ Good

    const user = {
      id: "XXX-XXX",
      name: "book",
    };
    
    product.id;
  • Do not contextualize the naming of the provided arguments to the functions.

    Easier perform massive find or replace.

    ❌ Bad

    function findProgramById(id) {
      ...
    }
    
    const programId = 'XXXX-XXXX';
    
    findProgramById(programId);

    ✔️ Good

    function findProgramById(programId) {
      ...
    }
    
    const programId = 'XXXX-XXXX';
    
    findProgramById(programId);

    ❌ Bad

    function findProgramById({ id }) {
      ...
    }
    
    const programId = 'XXXX-XXXX';
    
    findProgramById({ id: programId });

    ✔️ Good

    function findProgramById({ programId }) {
      ...
    }
    
    const programId = 'XXXX-XXXX';
    
    findProgramById({ programId });

🔝 back to top

Timestamps

  • Use verbAt.

    ✔️ Good

    const createdAt = new Date().toISOString();
    const updatedAt = new Date().toISOString();

🔝 back to top

Functions

  • camelCase for functions.

  • Recommended use verbAdjectiveContextStructureHow pattern, where verb stick to action, adjective act as modifier for a context, and context is the object being interacted with. Adjective, context, structure and how are optionals. [+]

    ❌ Bad

    function programGetActiveById(programId) {
      ...
    }

    ✔️ Good

    function findActiveProgramById(programId) {
      ...
    }
  • Skip get prefix when function is returning a boolean.

🔝 back to top

Vocabulary

Prefix Description
to Convert object to another type.
plus Returns a copy object with the amount added.
minus Returns a copy object with the amount subtracted.
with Return a copy with element target.
of Returns an instance where the factory is primarily validating the input parameters, not converting them.
from Converts the input parameters to an instance of the target object, which may involve losing information from the input.
parse Parses the input string to produce an instance of the target class.
format Uses the specified formatter to format the values in the temporal object.
at Combines this object with another.
get Return a part of the state of the object.
list Return a collection of part of the state of the object.
create Returns a new instance on each invocation.
build Returns a new instance where many separate pieces of information are combined in some way.
generate Returns a new instance where a calculation is used to produce a value from an input.

Partial borrowed from oracle documentation. [+]

🔝 back to top

Constructors

  • PascalCase for constructors.

    ✔️ Good

    class GoodGreeter {
      name;
    
      constructor() {
        this.name = 'hello';
      }
    }
  • Where appropriate, use a compound word for the naming. The second part of the derived name can be the name of the pattern. [+]

    ✔️ Good

    class GetItemBySlugUseCase {
      constructor() {}
    }
  • Infer context on naming class methods.

    ❌ Bad

    class ProgramStore {
      getProgramById(programId) {
        ...
      }
    }

    ✔️ Good

    class ProgramStore {
      getById(programId) {
        ...
      }
    }

🔝 back to top

Enumerations

  • PascalCase for enumerations and value names. [+]

  • Singular type name.

    Enumerations are used to represent a fixed number of possible values.

    ❌ Bad

    const codes = {
      notFound: 'NotFound',
      badRequest: 'BadRequest',
    };

    ✔️ Good

    const Code = {
      NotFound: 'NotFound',
      BadRequest: 'BadRequest',
    };

🔝 back to top

Measures

  • Use measures as suffix.

    ❌ Bad

    const REQUEST_TIMEOUT = 2000;
    const MIN_COMPRESSION = 64;

    ✔️ Good

    const REQUEST_TIMEOUT_MS = 2000;
    const MIN_COMPRESSION_BYTE = 64;

🔝 back to top

Counts

  • Use count as suffix to indicate quantity. [+]

    count is shorter than numberOf.

    number is ambiguous. It could be a count, or an index, or some other number.

  • Use index as suffix to indicate sequence number 0..n.

    ❌ Bad

    const MAX_PROGRAMS = 5;
    const PROGRAM_NUMBER = 2;

    ✔️ Good

    const MAX_PROGRAM_COUNT = 5;
    const PROGRAM_INDEX = 2;

🔝 back to top

Public modules

  • Don’t use descriptive names for public modules.

    Descriptive names are anti-democratic. [+].

🔝 back to top

Asynchronous

  • Use sync suffix for synchronous function when you have asynchronous version of the same function.

    NodeJS implicit convention. [+]

  • Use when prefix for variables. [+]

    It sounds like the Promise then method.

    It should mean ‘when this happens’.

    ✔️ Good

    async function listPrograms() {
      ...
    }
    
    const whenPrograms = listPrograms();
    const programs = await whenPrograms;

    ❌ Bad

    function listPrograms() {
      ...
    }
    
    async function listProgramsAsync() {
      ...
    }
    
    const whenPrograms = listProgramsAsync();
    const programs = await whenPrograms;

    ✔️ Good

    function listProgramsSync() {
      ...
    }
    
    async function listPrograms() {
      ...
    }
    
    const whenPrograms = listPrograms();
    const programs = await whenPrograms;

🔝 back to top

Generators

  • Use gen suffix when you have Generator version of the same function.

  • Use iter prefix for variables. [+]

    ❌ Bad

    function* listProgramsGen() {
      ...
    }
    
    const iterPrograms = listProgramsGen();

    ✔️ Good

    function* listPrograms() {
      ...
    }
    
    const iterPrograms = listPrograms();

    ✔️ Good

    async function listPrograms() {
      ...
    }
    
    async function* listProgramsGen() {
      ...
    }
    
    const iterPrograms = listProgramsGen();
    const programs = [];
    
    for await (let program of iterPrograms) {
      programs.push(program);
    }

🔝 back to top

Formatting conventions

Curly braces

  • Delimit scope blocks with curly braces. [+]

  • Opening brace goes on the end of the current line, and the last brace in the new line.

    Known as egyptian brackets. [+]

    ❌ Bad

    if (true) doSomething();

    ✔️ Good

    if (true) {
      doSomething();
    }

🔝 back to top

Block scopes

  • Space between block scopes.

    ❌ Bad

    if (true) {
      doSomething();
    }
    if (true) {
      doSomethingElse();
    }

    ✔️ Good

    if (true) {
      doSomething();
    }
    
    if (true) {
      doSomethingElse();
    }

🔝 back to top

Programming practices

  • Be consistent with existing code.

🔝 back to top

Exports

  • Use named exports.

    To avoid interoperational problems between ES Modules and CommonJS. [+]

    ❌ Bad

    export default class MyClass {
      ...
    }

    ✔️ Good

    export class MyClass {
      ...
    }

🔝 back to top

Functions

  • Use function syntax for functions.

    In general function syntax is preferred, in particular for top level functions (to avoid TDZ issues, export const foo = () => {} function will not be available to be called unless the module where it came from has already been evaluated, otherwise you'll get the temporal dead zone error, happens with circular dependencies). [+]

  • Use arrow functions for callbacks.

    Arrow syntax should be limited to closures.

🔝 back to top

Decorations

  • Avoid use of annotations for decorations.

    Are executed at time of interpretation, that could create inconvenience when you are injecting dependencies which need be initialized at tame of class instance creation (e.g.: happens on resolving with auto-wire).

    ❌ Bad

    class MyClass {
      @decorate()
      doStuff() {
        ...
      }
    }

    ✔️ Good

    class MyClass {
      constructor() {
        this.doStuff = decorate(this.doStuff);
      }
    
      doStuff() {
        ...
      }
    }

🔝 back to top

Interfaces

  • Use interfaces over aliases where possible.

    ❌ Bad

    type FnAsync = (...args: any[]) => Promise<any>;

    ✔️ Good

    interface FnAsync {
      (...args: any[]): Promise<any>;
    }

🔝 back to top

Constructors

  • Use classes if you have more than one method and shared state.

    ❌ Bad

    class RedisClient {
      constructor(baseURL) {
        ...
      }
    
      create() {
        ...
      }
    }
    
    const redisClient = new RedisClient(baseURL);
    const cacheClient = redisClient.create();

    ✔️ Good

    function createRedisClientCreator(baseURL) {
      return function createRedisClient() {
        ...
      }
    }
    
    const createRedisClient = createRedisClientCreator(baseURL);
    const cacheClient = createRedisClient();

🔝 back to top

Monorepos

  • Every package should contain all the needed dependencies.

    Doing this allows us to cleanly decouple projects (packages) from one another, since you don't have to merge all their dependencies in one huge unmaintainable list. [+]

🔝 back to top

Comments

  • Comments are code smell, when comment describes what the code is doing.

    From a philosophical point of view, each line of code contains a technical debt for further support. Only the final functionality is the value. And if you can implement it without a single line (of commentary) at all, then everything is perfect. Otherwise, you should always have the WHY / WHY motive you added it for. Theoretically, this motive should be indicated in the commentary. The WHAT question is usually resolved by meaningful of the identifiers of classes, functions and variables. The question HOW should be clear from the code itself (also theoretically). [+] [++]

🔝 back to top

Composition

  • Use composition over inheritance.

🔝 back to top

Goal

If we start from the fact that programming is a chain of taking decisions, the aim of this guide is to inspire you and facilitate to take such decisions.

Following guide is a set of different sources most of them conveniently linked.

🔝 back to top

Other projects

  • Daisugi is a minimalist functional middleware engine.
  • Kintsugi is a set of utilities to help build a fault tolerant services.
  • Kado is a minimal and unobtrusive inversion of control container.
  • Oza is a fast, opinionated, minimalist web framework for NodeJS.

🔝 back to top

License

MIT