Coding Guidelines
Core principles
- Clarity over cleverness: Code should be easy to read and maintain.
- Predictability: Avoid unexpected behavior.
- Minimalism: Write only the code you need.
- Safety first: Catch errors early and prevent runtime failures.
- Simplicity and explicitness: Prefer straightforward solutions over complex or implicit behavior.
General rules
- No implicit type coercion: Use strict equality (
===
,!==
). - No magic numbers or strings: Use named constants.
- No global variables: Use modules to scope your code.
- No side effects in functions: Keep functions pure whenever possible.
- Limit function complexity: Functions should have a single responsibility.
- Use explicit types: Always type function parameters, return values, and variables.
- No dynamic code execution: Avoid
eval()
and dynamic imports. - Error handling: Always handle errors explicitly.
- Immutable data: Favor immutability where possible.
- Test coverage: Every function must have tests covering normal and edge cases.
Typescript-specific rules
- Enable strict mode: Always use
"strict": true
intsconfig.json
. - Avoid
any
type: Use specific types or generics instead. - Use readonly: Mark properties and arrays as
readonly
when they shouldn’t change. - Type narrowing: Use type guards and
unknown
instead ofany
. - No implicit any: Avoid missing type annotations.
Code structure
- Function length: Max 20 lines per function.
- File length: Max 300 lines per file.
- Module exports: Export a single default entity unless multiple named exports are justified.
- Inline code simplicity: Avoid deeply nested code blocks; use early returns.
Naming conventions
- Use camelCase for variables and functions.
- Use PascalCase for classes and types.
- Use UPPER_CASE for constants.
Security
- Never expose sensitive data in logs.
- Never expose environment variables or secrets/keys that should remain private.
- Validate all user inputs.
- Sanitize any data used in dynamic contexts.
Testing
- Use Jest or Vitest for unit tests.
- Test every function with valid, invalid, and edge-case inputs.
- Mock external dependencies.
Control flow and abstractions
- Use simple and explicit control flow: Avoid complex or nested control structures.
- Minimal use of abstractions: Introduce abstractions only if they clearly improve code clarity.
- Beware of abstraction costs: Every abstraction has a cost. Use them judiciously.
- Predictable loops: Use standard loops with clear bounds.
- Clear conditionals: Prefer straightforward conditional statements over ternary chains.
Assertions
- Use assertions to catch programming errors early: Verify assumptions that should never occur during normal operation.
- Fail fast: Assertions should trigger immediate failures when assumptions are violated.
- No production assertions: Avoid assertions in production code; use proper error handling for expected scenarios.
- Not for regular conditions: Do not use assertions for normal conditional checks. Use standard control flow instead.
Third-party packages
- Avoid excessive dependencies: Only use third-party packages when they provide significant value.
- Prefer in-house solutions: Many tasks can be accomplished with simple, custom code.
- Evaluate package reliability: Ensure that any package used is well-maintained, secure, and aligns with the project’s needs.
- Minimize package bloat: Reducing dependencies helps improve performance, security, and maintainability.
Last updated on