Code Quality and Static Code Analysis
Introduction
Maintaining code quality is a crucial aspect of software development. Ensuring our codebase is clean, maintainable, and free from errors is a collective responsibility. This document outlines our policies, best practices, tools, and metrics for maintaining the highest code quality in our project.
Tools and Technologies
Backend (Django):
Linter:
flake8
exclude = .git, .gitignore, *.pot, *.py[co], __pycache__, venv, .env, .coverage, openIMIS/wsgi.py, dist, openIMIS/migrations, ../**/migrations/*.py, # Relative paths for other modules ../**/.git, ../**/.gitignore, ../**/*.pot, ../**/*.py[co], ../**/__pycache__, ignore = # At least two spaces before inline comment. E261, # Too many blank lines (3). E303, # Ambiguous variable name. E741, # Unused imports - sometimes required in modular version, fixes in __init__.py may break builds # Ideally added for every __init__ file manually but for the time it's added globally F401 max-line-length = 120
Static Code Analysis:
SonarQube
Unit Testing:
Django's built-in testing tools
Frontend (React):
Linter:
ESLint
{ "env": { "browser": true, "es2021": true }, "extends": [ "plugin:react/recommended", "airbnb" ], "overrides": [ ], "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "plugins": [ "react" ], "rules": { "react/prop-types": "off", "no-shadow": "off", // disabled due to use of bindActionCreators "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], // disabled due to naming consistency with other modules "import/no-unresolved": "off", // disable due to module architecture. For modules most references are marked as unresolved "max-len": ["error", { "code": 120 }] } }
Static Code Analysis:
SonarQube
Workflow
Pre-push/Pre-PR:
Developers should run
flake8
for Django code andESLint
for React code before pushing their changes.Ensure unit tests are passing for backend code.
During CI/CD pipeline test execution tests for all core modules are executed, not only for single module.
CI/CD Pipeline:
Upon creating a PR or pushing to specific branches, our Github Actions CI/CD pipeline will automatically:
Run the linters (
flake8
&ESLint
)Execute all backend unit tests
Perform static code analysis through SonarQube
Results Visualization:
Linter and unit test results are directly reported in the Github Actions CI/CD pipeline logs.
SonarQube results, including code coverage, code smells, and potential bugs, are visualized in our SonarQube dashboard linked with the project.
Policies and Practices
Code Quality Metrics:
All PRs should pass the default quality gates set in SonarQube.
Linter errors (from both
flake8
andESLint
) must be addressed.Backend unit tests should always pass.
We encourage the community to provide feedback on additional metrics and standards. Open a discussion thread or issue for suggestions.
Code Review Process:
Every PR undergoes a review by at least one core developer.
Address all failing checks in the CI/CD pipeline before requesting a review.
Reviewers should prioritize the logic, efficiency, and alignment with the project's goals.
SonarQube's feedback should be considered advisory. Address critical and high-severity issues. For minor issues, reviewers' discretion is advised.
Community Involvement:
Given the open-source nature of the project, community feedback on code quality metrics and practices is invaluable. Please contribute to the discussion threads or open new ones with suggestions.
Code Review
Every piece of code, irrespective of its origin, undergoes a review process:
Reviewer Requirement: At least one review from a team member is mandatory for every pull request.
Checklist for Reviewers:
Code follows the project's style guidelines.
Changes are consistent with the overarching architecture.
Adequate tests are in place.
All tests pass.
No direct database calls; always use the ORM.
Check for potential security vulnerabilities.
Ensure no hardcoded values; always use constants or environment variables.
Code is DRY (Don't Repeat Yourself).
Comments are clear, and no commented-out code is present.
Backend CI/CD Pipeline
All backend modules should use pipeline defined in the openimis-be_py in order to execute code analysis (Workflow Definition).
Flow executed by the CI Job:
Example setup: Social Protection Module
Setup variables:
SONAR_TOKEN - either taken from organization settings or from sonar project site
SONAR_PROJECT_KEY - key from Sonar, usually it’s openimis_<repository_name>
SONAR_ORGANIZATION - organization key, always openimis-1
for modules in organization
SONAR_PROJECT_NAME: repository name
SONAR_PROJECT_VERSION: 1.0
SONAR_SOURCES: social_protection
SONAR_EXCLUSIONS: excluded directories for coverage, by default "**/migrations/**,**/static/**,**/media/**,**/tests/**"
Workflow Overview
Trigger: The workflow is triggered through
workflow_call
. It can receive several optional secrets and inputs, likeSONAR_TOKEN
andSONAR_PROJECT_KEY
, allowing for flexibility and customization.Jobs:
build_backend
: Prepares the backend for testing.ci_module_mssql_tests
: Runs all tests on Microsoft SQL Server.ci_module_psql_test
: Runs all tests on PostgreSQL.flake-8-linter
- This job is responsible for running code style checks using Flake8 on Python code.sonar_scan
- This job is responsible for running static code analysis using SonarCloud to identify code vulnerabilities, code smells, and to analyze the coverage report.
Jobs Details
build_backend
Environment: Uses Ubuntu 20.04 and Python 3.8.
Dependencies: Caches Python dependencies to speed up future runs.
Cloning: Clones the openIMIS Backend from the
develop
branch.Configuration: Updates configurations using
jq
for JSON processing and replaces or adds the current module intoopenimis.json
.Artifacts: Compresses and uploads Python
site-packages
andopenimis
code as artifacts.
ci_module_mssql_tests
Environment: Uses Ubuntu 20.04 and Python 3.8.
Dependencies: Downloads previously cached Python packages and installs them.
Database Setup: Uses a Microsoft SQL Server container and initializes it with the openIMIS database.
Testing: Runs Django tests against this SQL Server database. It utilizes environment variables to specify the database details.
ci_module_psql_test
Environment: Similar to the
ci_module_mssql_tests
job but targets PostgreSQL.Database Setup: Uses a PostgreSQL container for the database.
Testing: Similar to the SQL Server job but for PostgreSQL.
flake-8-linter
Environment: Uses Ubuntu 20.04 and Python 3.8.
Dependencies: Requires builded backend and it’s artifacts.
Configuration: Uses .flake8 setup from assembly module. Linter is done for whole module not only new code.
sonar_scan
Environment: Uses Ubuntu 20.04
Dependencies: Require coverage report and flake8 report generated in previous steps.
Configuration: Module details passed using execution variables. Quality Gateways can be configured directly on sonarCloud.
Notes on Code Quality Checks
The use of
jq
ensures that the JSON configuration (openimis.json
) is programmatically editable, maintaining a level of correctness.The use of environment variables and GitHub secrets, like
SONAR_TOKEN
, ensures security and confidentiality.The caching mechanisms enhance performance and reduce build times, aligning with best practices.
Sonar Quality Gateway
Currently all modules are using default Quality Gateway provided by Sonar. It expects code to:
Have 80% coverage
< 3% duplicates
< 5% Technical Debt
No Bugs
No Security Vulnerability
Suggestions for further process improvements
Disagreements & Escalation
While we anticipate most discussions to be constructive, disagreements might arise. When they do:
Both parties should discuss their perspectives in the PR comments.
If no agreement is reached, a third-party core team member will intervene and provide feedback.
As a last resort, the project maintainers have the final say on including or excluding a piece of code.
Refactoring
As the project evolves, refactoring becomes necessary to maintain code quality:
Regular Refactoring Sprints: Every three months, a sprint dedicated to refactoring and addressing tech debt can be beneficial.
Community Involvement: While community contributions primarily focus on features and fixes, we should encourage community members to participate in refactoring. Picking up smaller refactoring tasks can be a great way to familiarize oneself with the codebase.
Module CI
Add functionality allowing passing openimis.json as call paramter. Currently it’s fixed for develop branch which will not be optimal for all builds. Ideally all modules should be executed with theirs dependencies and modules that are depending on given one in order to make sure that changes didn’t affect other modules.
Did you encounter a problem or do you have a suggestion?
Please contact our Service Desk
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. https://creativecommons.org/licenses/by-sa/4.0/