Coding guidelines

From Seedbury Square
Revision as of 13:05, 7 January 2026 by Mgm-seedbury (talk | contribs)

Parameters

Parameters will be divided into 3 broad categories, parentProps, required parameters and optional parameters.

Required parameters will not have default values, they must be provided as arguments every time the class or function is used.

Optional parameters may have default values, but they may also have no default value. No default value is preferred, and the possibility of an undefined value will be explicitly and intentionally handled. Some examples of correct default values for optional parameters include (but are not limited to):

  • The parameter is a constrained list of possible values (ex. size: 'small' | 'medium' | 'large').
  • The parameter is of type boolean, in which case false is preferred to undefined.

We will take advantage of type checking to help make sure the classes and functions are being used correctly. In the jsdocs, required parameters are defined as usual and optional parameters will be enclosed in brackets. Example:

* @param {AppProps} parentProps
* @param {object} params
* @param {number} params.requiredParameter
* @param {boolean} [params.optionalParameter]

This will make VSCode flag as an error any situation in which the class or function is being called without all required parameters.

Classes

When creating a class, the constructor will generally have 2 parameters: parentProps and an object called 'params' containing the rest of them. Some classes may not need parentProps, or a params object, or either of them; in those cases they are not added, which means some specific classes may instead have 1 or 0 parameters. Example of the typical class:

class Example {
  constructor(parentProps, {requiredParameter, optionalParameter = true, anotherOptionalParameter}) { }
}

When a class has a params object and all of the parameters are optional, the entire object must be given the default value of empty object. If at least one parameter is required, this default value should not be included. Example:

constructor(parentProps, {optional1, optional2 = 'small'} = {})
constructor(parentProps, {required, optional})

Methods and other functions

Parameter Structure

While classes will strictly adhere to the parentProps + paramsObject structure, methods and other functions (helpers or baseElements) will require a case by case analysis to determine the structure of parameters. A destructured object as the sole parameter should be the starting point; analyzing the behavior of the function and the most common way it will be invoked will inform any changes to be made. Some patterns to be considered when moving away from the sole object parameters are:

  • The method is a setter - it clearly states in the name what the value sent will be, and it should never be called without a specific value available. In this case it makes sense to have a singular required parameter followed by the destructured object of optional parameters (if any). Example:
setChecked(checked, { executeCallback, disable = false } = {}) { }
setEntity(entity) { }
  • The function is a baseElement that will receive a value in the overwhelming majority majority of cases. Example:
getParagraph(string, { size = 'medium', bold = false } = {}) { }
  • The function has a clear purpose that requires more than one required parameter and the label of those parameters is trivial. Example:
addNumbers(number1, number2, { optional } = {}) { }

This does not mean that all required parameters in methods and functions go outside the destructured object. Even while being required, having them inside the params object forces the parameter to be clearly labeled, improving legibility. Example:

addTeamMemberToProject(123, 456, 789)

vs

addTeamMemberToProject({ teamMemberId: 123, projectId: 456, selfEntityId: 789 })

Amount of Parameters

Something to consider in methods and functions specifically is an appropriate amount of parameters.

As a general rule of thumb if a function has more than 3 required parameters, it is likely to be doing too many things. Functions should address a singular objective, and if they are growing to the point of needing more than 3 required parameters, chances are it is due for a refactor and breaking that functionality into more than 1 function/method.

Private properties and methods

Making properties and methods of classes private grants us a higher level of code security and discourages using functions and variables intended only for internal logic outside the class, possibly editing by reference, breaking internal logic, and introducing hard to track bugs. It makes the decision to allow certain methods to be used outside the class intentional.

In general, all properties should be private (except for the .view property, which should be public but read-only) and if a certain value is used outside of the class a getter and a setter function is created.

Getters and Setters

When a private value needs to be read outside of the class, a getter method is created. It is always named get+variableName. Getter methods usually have no parameters. If the value to be read is an array or an object, it should be cloned before returning, creating a new identical object but with a different pointer. This way, if another class uses the getter method of this class, it can modify it for their own use without mutating the object used internally in this class, possibly causing bugs.

When a private value needs to be mutated from the outside, a setter method is created. It is always named set+variableName.