HipSquare Collaboration Guidelines and Coding Conventions #
The guidelines and conventions outlined in this document are focused on collaborating on code. Please also see our Code of Conduct for our general collaboration in HipSquare.
Versioning #
Semantic Versioning #
If Continuous Integration/Deployment isn’t possible, we follow the principles of semantic versioning.
<major>.<minor>.<patch>
Breaking changes result in a major version update.
Features (with or without Bugfixes) result in a minor version update.
Hotfixes result in a patch.
Examples:
0.0.11.2.3
Branching #
Git Flow is a nice (but not mandatory) framework, which follows the principles of semver.
If a ticketing tool is in use (Gitlab issues, Jira, …), it’s desirable to have a reference to the ticket or story in the branch name.
New branches should always be started from the latest base branch (main or develop).
The branch name should contain the type of ticket, the project abbreviation and the ticket number:
<type>/<project>-<number>
Valid types would be:
chorefeatfixhotfix
To avoid merge request being too large to review it’s reasonable to have a base branch to work on. Further outcoming branches will be merged back into the base branch.
An additional descriptive suffix is advised.
Examples:
feat/HIP-123chore/HIP-1
No code gets merged into main without a Merge Request.
No Merge Request gets merged without review.
No Merge Request gets self-approved.
Commit Messages #
A format, such as described in the provided link is desirable.
Commit messages would then follow the format:
<type>(<?scope>): <subject> #scope is optional
Valid types would be:
chorefeatfixhotfix
In a monorepo the scope can be one of the packages.
In a larger application it can be a certain module, such as auth or user or product or the name of the app.
The message itself should be starting with a descriptive verb in the present tense.
Examples:
feat(auth): add oauth flowfix(profile): prevent flickering
Hooks #
Given that a tool such as Husky is being used:
pre-commit:prettier:- To avoid unnecessary commits formatting should be amended to the current commit
- Use:
git commit --amend --no-edit
Merge requests #
The only way code reaches the main branch of a repository is via merge requests. We require at least one approving review for any merge requests.
Our attitude towards merge requests #
Merge requests play an important role for several reasons:
- On the surface, they are a quality gate.
- They help in knowledge exchange. Reading other people’s code gives insights on both coding styles and techniques as well as domain-specific knowledge of the given project. Providing and receiving feedback helps to grow.
- They are a token of appreciation. Both a well-described MR and a detailed and appreciative review show appreciation for each other’s work.
To get the most out of the merge request process, we approach merge requests with a positive mindset:
- Reviewing a merge request usually means a context switch and extra time spent outside of “regular duties”. That’s why, as MR authors, we owe the reviewers a well-described merge request.
- Reviews, even and especially those with a lot and seemingly nitpicky findings, are incredibly valuable feedback. The reviewer invested precious time to do the review and document the findings, so as MR authors, we appreciate that as a chance to grow together.
- Creating a merge request and exposing code to the world makes an MR author vulnerable. As a reviewer, I appreciate that vulnerability and provide feedback in a way that is appreciating and loving.
- Neither reviews nor reactions to reviews should ever be harsh, aggressive or condescending.
Writing good merge requests #
We work with merge request templates wherever possible. As a merge request author, I will at least describe:
- Why I changed what and what ticket(s) are solved,
- how I made those changes and, if the rationale is not obvious, what the rationale was,
- how the reviewer can test the changes themselves. The instructions should be sufficiently detailed for a junior developer who is versed in the general technology used, but not the specific project. The reviewer should be made able to perform review steps and judge if the changes proposed in the MR work or they do not.
Merge request titles #
We use merge request titles that comply with our commit message format.
Coding Conventions #
Common rules: #
DRYKISS
Use descriptive names for functions and variables.
Add comments and documents where necessary.
Add tests.
Add proper typings for arguments and return values.
Function names should be descriptive and start with a verb, such as getUsers or convertImage.
The usage of a prettier and/or a linter is required.
This keeps the format consistent, while shifting the task of formatting to the IDE.
Formatting #
- We use double quotes (
"), not single quotes (') for strings. - We use spaces, not tabs, for white spaces.
- In JavaScript/TypeScript, we use semicolons to delimit function calls, variable definitions, …
Names #
- We use
camelCasefor variable names and function names.
Rules to be discussed: #
- line length
Clean and Working Code #
When setting up new projects, let’s make sure that at least two quality gates are in place:
- There should be a
pre-commithook that at least runs a linter. If it doesn’t take too long, it’d be nice to add tests and a build also, but in most projects, these will be too long running operations. - On top of that, there needs to be a CI pipeline (optimally: GitLab CI) for each project. The CI needs to:
- run the linter
- run all tests
- perform a build
- The CI needs to be run for every merge requests. Merge requests cannot be merged without a succeeding CI pipeline for their given feature branch.
Code documentation #
Code documentation is a helpful practice that can improve the experience of everyone working on the project - other developers, non-developers (e.g. through storybook), and yourself. IDEs like VSCode natively use code documentation for tooltips, quickly informing developers about what a function or component is supposed to do.
JSDoc #
We use JSDoc for code documentation.
Style and scale #
Documentation should be short and to the point. Usually a sentence or sentence fragment will suffice. If additional information is available online, use the @see tag followed by a link. You can use markdown to style the link (example: @see [Google](https://www.google.com) for any further questions).
Maintanance #
Be aware that documentation needs to be maintained. Over time code may change, and there is no way to automatically verify that old documentation is still correct and useful. Check if documentation needs to be updated before you make a merge request.
React components #
Every react component should have a JSDoc description. Every prop should have a description as well.
Example:
export interface StatusCardProps {
/** (optional) Decider function for the overall status */
overallStatusDecider?: (statusModels: StatusModel[]) => StatusType;
/** Data of the substatuses */
subStatuses: StatusModel[];
/** Card title */
title: string;
}
/** Card for displaying sub-statuses and a computed overall status */
const StatusCard: FC<StatusCardProps> = ({
Storybook #
The Storybook docs plugin automatically makes the component documentation available in the Storybook UI.
If necessary use argTypes to fine-tune how props are displayed in Storybook. You can also hide technical props from the storybook display which would be confusing or distracting from the core functionality of the component. Do use your best judgement though, oversimplification might be misleading.
See Document for stakeholders for further information on documenting in Storybook.
Function, classes and members #
Write documentation at least for every class and function, which is exported and as such reusable.
Write documentation at least for each public method of (exported) classes.
For functions and members: Use @param tags to describe parameters and the @returns tag for the return value.
DRY #
Don’t repeat yourself.
Common and repetitive patterns should be abstracted and extracted into helper functions or components.
KISS #
Keep it stupid simple.
Lengthy and complex methods and functions should be avoided.
Exit early. Error cases should be managed early in a function to avoid unnecessary computation.
Try to avoid chained promises.
Use async/await instead.
Try to avoid else and/or nested if/else statements.
Use only a single if if applicable.
Try to avoid any at all.
Use proper types.
Typings #
Types increase code quality and readability tremendously. It always an easier introduction to unknown or unfamiliar codebases, while also serving as a primitive documentation.
When creating a new project the languages and tools using types should always be favored over untyped equivalents.
Languages with dynamic typing, but support for strong/static typing should be used with their respective option. For instance, TypeScript should always be preferred over JavaScript.
Projects should always have and derive types.
Tests #
Try to follow Test-driven development(TDD).
A test suite should test everything that could possibly break.
Try using a coverage tool.
Test boundary conditions. (We often get the middle of a logic right but misjudge the boundaries)
The test cases should be extended if a bug gets fixed which was caused due to a flaw in the logical parts of our codebase.
CI, CI and Repositories #
We aim to keep all CI-/CD-related things close to our source code and hence use GitLab CI for our projects. This includes:
- The CI pipelines for merge requests,
- CD to perform builds, deployments and maintenance jobs,
- storing artifacts (use HipSquare / SquareTrip NPM Packages · GitLab for NPM packages and your individual project’s container registry for Docker images).
All CI jobs need to be described in files inside GitLab (either the project repository or, if it makes sense, some overarching repository). Let’s try to avoid using any other CI/CD tooling (no Jenkins anymore 🥳).