Coding Guidelines - OpenSlides/OpenSlides GitHub Wiki
Following these guidelines will let writing readable and maintainable code and developing a resilient software.
The goal when developing software is to achieve high cohesion and loose coupling.
Cohesion is the degree of expediency. "High cohesion" means a very useful class. A high cohesion can be achieved by the Single Responsibility Principle. Build one class for one use-case.
Also,
- mark every function and variable as private, unless another instance has to use it (information hiding)
- mark every variable as constant, unless it will be changed
- bundle shared data and classes for the same concern into a module. Each module should interact with another module only in one way.
Coupling is the degree of dependencies of a class. "Loose coupling" can be achieved by these things, among others:
- Make use of several patterns (publish-&-subscribe-, message-exchange-pattern)
- Use interfaces to expect and use functions (port-adapter-pattern)
- Separate the construction of classes to a dedicated mechanism, like dependency injection, the usage of the builder-pattern or singletons
Inheritance is an anti-pattern. So, to avoid the usage of inheritance, implement classes, which fulfill a use-case and make use of their functions. Do never use multi-inheritance and try to inherit only one time.
To fulfill the single responsibility principle, every class must do only one thing. To know, if a class does only one thing, try to describe the functionality of the class. If there is an "and" in the description, it is a clue, that the class does more than one thing.
When implementing a function, follow these steps:
- Each function should have a maximum of twenty lines. If this maximum will be exceeded, it is an indication that a part of the function could be extracted as an additional function, which can be called. However, it is not bad, if a function have more than twenty lines, but hold it readable.
- Each function should do only one thing. Sometimes, to achieve a feature, multiple things have to be done. Then, there can be a function, which makes use of multiple functions and delegates data between them.
- Each operation block should have a maximal depth of two. This means, a block (of an
if
,for
, etc.) should be surrounded only one time by another block. If more blocks are needed, extract these blocks into another function and call it. - Work on class variables. If it is necessary, that a function takes parameters (for example in case of a builder-/factory-function), try to use one or maximal two parameters. If it is necessary, that a function takes more than two parameters, use for example an object that share data.
Do not try to return any error codes. Something like { isValid: false }
is an anti-pattern. Instead, throw an error. The best way is to create custom error-classes, which will be thrown. Thus, code can be executed depending on the class.
Use descriptive names for every variable, function and class. Try to describe the usage of a variable, function or class by its name. For example, use for variables of the type boolean or functions, which returns a boolean, something that suggests, that it can be asked. E.g. ask yourself "If it ...".