Coding Standards - SeedCompany/cord-docs GitHub Wiki
General
Pull Requests (PRs)
Each PR should deal with one very specific change or piece of functionality. One user story will typically align with a single PR, however it is possible that it may make more sense for multiple PRs if the story is larger. One way to determine the number of PRs is to think about whether ALL of your changes have to be deployed at once, or if there is opportunity/reasoning/need for certain changes to be deployed in isolation. If so, then multiple PRs may be warranted.
Commits
The proper way to commit code into a codebase for a single PR is to break each addition/modification/deletion into bite-sized, logical chunks. An entire user story and/or PR might be accomplishing one thing overall, but can and should be separated into multiple commits for two reasons:
- It allows the code review process to be much simpler as each logical piece can be reviewed separately
- It allows the possibility of isolating and/or reverting explicit changes in the future
Functional Quality:
Make sure that all of the functions are implemented and all of the Acceptance Criteria are checked/tested
- Unit testing (Dev, JEST)
- Regression Testing (Prod)
- Smoke Testing (Prod or/and Dev)
- The PR reviewers need to validate the functional quality and developers demo the functionality to the code reviewer when it’s needed.
Structural Quality
Consistency:
Commenting (optional):
- Purpose
- Description
Naming conventions: camelCase, clear, and descriptive, enforce homogeneous naming conventions.
- New folders and files (kebab case)
- Functions, variables (camelCase)
- Enums (pascal case)
- APIs
- Class (pascal case)
- UI Components (pascal case)
Version Control and Branching Strategies
- Branching (type/MondayTicketID-description, ex., enhancement/1234-new-profile-page)
- Commit messages (keep the commit message concise and to the point. A good commit message should be a brief summary of the changes):
1st line is the title to indicate what has been done with the ticket number and an imperative verb (ex., 0654 - IRP - Remove/move Impact Report Date). 2nd part the body to provide a brief explanation of why the change is needed or the problem it addresses (optional).
- Link the Monday ticket from the Description of the PRs
Simplicity, scalability, reusability, & maintainability:
- Break code into smaller, simple, self-contained modules or functions, each responsible for a single task or concept. (Single-responsibility Principle)
- Avoid Code Duplication.
- Avoid deeply nested statements. Use techniques like guard clauses and early returns to make code more linear and less complex.
- SOLID principles - reduce code coupling, improve code reusability, and make the software more adaptable to change.
Portability
- Avoid containing "hard-coded" (literal) values referring to environmental parameters, such as absolute file paths, file names, usernames.
Reliability
- Avoid software patterns that will lead to unexpected behavior (Uninitialized variables, etc.)
- Methods, procedures, and functions doing Insert, Update, Delete, Create or Select must include error management.
- General error handling.
- Validate data from external sources, such as APIs and databases, to ensure it adheres to expected standards and formats.
- Avoid changing code if not needed (if it’s not broken, don’t fix it).
- If changes that are outside the scope of the task are needed, then open up another task/PR to do a refactor
Efficiency
- Write clean and optimized code by eliminating redundant operations, minimizing loop iterations, and avoiding unnecessary function calls. Profile your code to identify bottlenecks.
- Use efficient Data Structure to minimize space overhead. For example, use arrays instead of lists when the size is fixed.
- Allocate memory efficiently by avoiding excessive allocations. For example, pre-allocate memory for containers if the sizes are known in advance.
- Limit the scope of variables to minimize memory consumption.
- Load data or resources on-demand as needed.
- Manage external dependencies carefully. Import the right packages/external libraries only.
- Process data in smaller chunks rather than loading entire datasets into memory, especially for large files or databases.
- Use constants or enums instead of magic numbers and strings to reduce memory usage and make code more readable.
- Optimize database queries and indexes for efficient data retrieval.
Security
Because security is crucial, yet a vast topic, it can be difficult for one to asses things on their own. This is where dependency analyzers come into play.
- An example of a dependency analyzer is Snyk (but there are many other free alternatives).
- When adding a new dependency, it's recommended to check with Snyk (or other dependency analyzers) and make sure there are no outstanding vulnerabilities in the needed version (e.g. lodash latest: https://snyk.io/node-js/lodash)
Architecture design
- Identify the major components and modules of the system. Define their responsibilities, interfaces, and relationships.
- Define clear and well-documented interfaces for the components, specifying how they interact with each other. Contracts should outline the expectations and responsibilities of each component.
- Illustrate how data flows between system components and external systems. Describe communication protocols, data formats, and integration points.
- Consider how the architecture will support scalability and performance requirements. This may involve load balancing, caching, and database scaling strategies.
- Define the data storage strategy and database design, including data models, schema, indexing, and data access patterns.
- Specify the technologies and tools that will be used for different components and layers of the system. Choose libraries and re-usable existing functions.
- Address cross-cutting concerns such as logging, monitoring, error handling, and internationalization at the architectural level.
- When to use logging, what should be logged, and which log levels should be used?
- Outline the testing strategy for the architecture, including unit testing, integration testing, and performance testing. Describe how the architecture will be validated against requirements.
- Create comprehensive architecture documentation that is accessible to developers, testers, and other stakeholders. Effective communication of the design is essential.
- Clearly identify any architectural trade-offs and associated risks. Make informed decisions based on these trade-offs.