6.6 KiB
Coding conventions for nwaku
Background
This guide aims to document the most important coding conventions followed in the nwaku codebase. This includes, but is not limited, to styling, formatting and code organisation. Since most of the repo is based on Nim, this is the main focus of the guide. This should become a handy reference when contributing to the codebase and help us refactor existing code to improve overall consistency.
General
The nwaku codebase uses the Status Nim style guide for styling. This guide, in turn, is based on NEP-1. These two documents remain the ground truth for styling decisions. Some easily-overlooked guidelines from these two guides are repeated below.
Logging
Also see the Nimbus logging strategy.
-
Define a
logScopefor each module that uses chronicles logging. -
Nim chronicles' log topics should be understood as log message labels.
- Log topics should contain "whole words" separated by spaces. In the case of a composed word, it should follow the
snake_casestyle. - Log topics should be ordered from more general to less general (e.g.,
waku node message_store sqlite_store) - Multiple
logScopetopic labels are optional. More than one label should be avoided if it does not aid debugging. Think carefully of your use case. - Log scopes/topics are meant for humans and machines to find logs that belong together when debugging. If there's no debugging use case to separate a subgroup of logs, there's no reason for a sublevel. Logs for a protocol, even across different modules, can simply be scoped together with
waku <protocol>, e.g.waku store. - The modules' log topics within
wakumodule should havewakuas the first topic. This will allow any user of waku (as a library) to mute all logs by filtering thewakutopic. Thewhispermodule is the exception. - The apps' log topics should start with the name of the app (e.g.,
wakunode2 rest) - Waku protocols' log topics should be specified without the "waku" prefix (e.g.,
waku filter client)
- Log topics should contain "whole words" separated by spaces. In the case of a composed word, it should follow the
-
Logs that could be high rate (even under an error condition), should not be logged at a level higher than
TRACE. One rule-of-thumb here is that per-message logs must always beTRACE. -
Keep log messages as succinct as possible - very long log messages are often difficult to read in most log renderers. Rather split a long log into separate log messages.
Imports/exports
see related section in Status Nim style guide
- Nim
imports andexports are very difficult to keep clean, so do your part by continuously cleaning unused imports, keeping yourimportset minimal and yourexportset reasonable. - Avoid circular imports. The direction of importing is (usually) from a more specific module to a more general module, e.g. the "general"
wakunodemodule importing several more specific protocol modules (but never in the opposite direction), which may in turn import even more specificutilmodules. There should be no application-level modules imported in the waku protocol modules.- @lnsd compiled this partial list of rules to help avoid circular imports:
utilsMUST NOT import any module fromprotocolornode.protocolCAN import modules fromutils.protocolMUST NOT import modules fromnode.nodeCAN import modules fromutilsandprotocol.appsmodules (e.g.config.nim) MUST NOT be imported by any module.
- @lnsd compiled this partial list of rules to help avoid circular imports:
- use
std/prefix for anyimportfrom stdlib - use explicit
importpaths (e.g../for modules in same path)
Error handling
see related section in Status Nim style guide
- Use a top level
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
in all modules
- Use
Resultreturn type for each function where multiple failure paths exists - Use
Optreturn type for each function where return may be empty/null - Add an explicit
{.raises.}for each applicable public (*) function. Prefer usingResultreturn type and result error checks above exceptions and catchable errors. - Avoid using
{raises: [Exception, CatchableError]} - Don't rely on empty initialisation to check for empty return - use options!
Constants
- Constant names should be in
PascalCase.
Proc call/object property parenthesis conventions
Follow these rules for the sake of consistency and readability:
- Verbs must be followed by the parenthesis, since it denotes a "question" (e.g.,
isSomething(),isOk(),isNil()) - Nouns must go without the parenthesis. A
procwith a noun name should go without parenthesis. They describe "properties" (or "derived properties" in theprocwith a noun name) of certain object. (e.g.,Result.error,Result.value)
Unit testing
- Whenever a bug is encountered it's an indication that unit test coverage has not been sufficient. Ensure therefore that every bugfix is accompanied by extended unit tests that detects the issue and prove the fix.
- Unit tests should be deterministic. That is, it should not rely on values or conditions from the runtime environment, but should be exactly repeatable on any platform or environment. For example, a unit test that adds random sleeps to wait for another process to finish is nondeterministic (this process may take longer/shorter to finish in other environments). Tests that use "current time" are also nondeterministic (this value differs each time the test is run).
Miscellaneous
- Don't use Nim's
resultreturns (reasoning here) - Never dispatch futures with
discardorasyncCheck; useasyncSpawn - Try to avoid using
discardwhere error must be properly handled - Use
new()instead ofinit()for constructors ofref objecttypes - Avoid leaving trailing whitespaces, both at the end of a line and in empty lines. This rule is already specified in .editorconfig so it can be enforced automatically every time you save.
- VS Code: Install EditorConfig for VS Code extension. Now every time you save, trailing whitespaces are removed.
- Vim: Install editorconfig-vim