# cppTango: How to contribute?

Thanks for your interest in contributing to cppTango. We welcome every contribution, regardless how small or big. You are spending your time on making cppTango better and that is fantastic!

In this document we try to explain how you will get your contribution into cppTango in the fastest possible way. But first we would like to lay out how important decisions are made in the cppTango team.

# From idea to code

In general it is a good idea, to discuss additions and changes first in an issue with the existing developers. This avoids duplicated work, disappointments and can save you a lot of time. Maybe something close to what you need is already there, and can be reused for your change.

We don't need a signed CLA (contributors license agreement) from you.

# Contribution overview

- Create an issue [here](https://gitlab.com/tango-controls/cppTango/-/issues/new) that describes the problem or the improvement. This helps us to track progress and link merge requests to the problems and improvements.
- Fork the repository to your own user.
- Add your fork as new remote: `git remote add myFork git@github.com:<user>/cppTango.git`
- Create a new branch for your work. Always branch off cppTango's `main` branch. Only if you are proposing a **critical bug fix** for the latest 9.3.x version, then branch off the `9.3-backports` branch.
- Start hacking.
- Test your modifications locally on your system. See the document [test/README](https://gitlab.com/tango-controls/cppTango/-/blob/main/test/README) for instructions.
    - if you don't have your own CI minutes, contact us when you want to test it on Gitlab.

- [Create a merge request](https://gitlab.com/tango-controls/cppTango/-/merge_requests/new) (MR). Begin the description of the MR with `Draft:` or `WIP:`. This indicates that you are still working on the MR.
- Rebase you code frequently! This will ensure that you do not face merge issues at the end of your work.
- Once the CI passes, mark the merge request as ready and remove `Draft:`or `WIP:`from the description. You have rebased your code, haven't you?
- Before an MR can be merged, the modifications need to be reviewed.
    - You can either assign Reviewers yourself, if you know who should review your MR,
    - or Reviewers will be assigned by the cppTango team.
    - Each MR needs one reviewer from the [Code Owners](https://gitlab.com/tango-controls/cppTango/-/blob/main/CODEOWNERS) (see [Approval](#approval) below).

- The reviewer will go through the modifications that you made and provide feedback.
- Once the review has concluded, the reviewer will approve the MR.
- After approval, the MR will be merged.

# Merge request acceptance and merging

Thank you very much for your work and congratulations! You have made cppTango better. 🎉

And now you want to get these changes merged? Very nice!

In order to make this process as smooth as possible for you, here are a few hints for the path forward:

- All CI tests have to pass. If you have changed the behaviour of the code, you should really add new tests that verify the new behaviour as well.
  You don't need to run the tests locally, because CI is the reference anyway. So just create an MR and let CI
  handle that.
- Make your MR easy to review. This starts with explaining what it wants to achieve and ends with splitting
  the changes into logical commits where each commit describes the reason for the changed code.
- Try to keep the MR small. Nobody likes to review 500 changes in 30 files in a single MR.
  Chances are that people are unwilling to review large MRs. Please remember that MR reviews
  really take time.
- Be prepared to adapt your merge request to the review responses. Our code review is done to ensure better code
  quality and to communicate implementation details to newcomers. The reviews are not to annoy anybody or slowing down
  development.
- Adapting existing merge requests also involves force-pushing new versions as we don't want to have
  intermediate versions in the history.
- Every MR should have a `Close XXX` statement which points to the accompanying issue.
- The Milestone assignment for the MR and issue needs to be correct.

# Approval

In general each merge request (MR) needs an approval from one of the [Code Owners](#glossary)
listed [here](https://gitlab.com/tango-controls/cppTango/-/blob/main/CODEOWNERS).  If the reviewing Code Owner deems the MR sufficiently complicated, they may ask for a review from someone else as well (perhaps not a code owner).


# Assigning an issue or an MR

Assignees of an issue or MR are usually the ones who are actively working on it. The cppTango team will assign the issues and MRs to the contributors and team members whether during the weekly meeting or when the open issues and MRs are groomed. If you, as contributor, think that somebody should get involved, leave a comment in the issue or MR and tag the comment with that person's user name.

## Issues

Please assign an issue to yourself if you are working on it. In that way everyone knows who is working on what.

## Merge Requests

- For the development phase, the creator should assign the MR to themselves, mark the MR as draft and leave the reviewer blank.
- Once the MR is ready for review
  - The draft state needs to be removed from it.
  - Reviewers need to be added, including at least one of the [Code Owners](#glossary).  If you are unsure who should review your MR, leave a comment asking who should review and the cppTango team will add an appropriate reviewer at their next meeting.
  - Then the reviewer will handle the review.
- If you realise the MR needs more work after asking for a review, mark the MR as draft so that the reviewers know it is not ready yet.
- In most cases the reviewers will provide feedback, want something changed, etc. They will then leave a review using Gitlab's review feature to reflect that.
  - Once the requested changes are implemented, ask for another review using the Gitlab review feature.
- Once the reviewer is happy with the MR, they will approve the merge request.
  - If the assignee has merge permissions, the reviewer will leave it to the assignee to merge
  - Otherwise, someone from the cppTango team with permissions (possibly the reviewer) will merge it on the assignee's behalf
  - If the assignee is happy for the MR to be merged as soon as it is ready, they can use Gitlab's auto-merge option if they have the permissions

If you have any questions, please get in touch!

# Issues and MRs with bigger impact

It might happen from time to time that an issue or an MR will have a significant impact on cppTango or even on Tango Controls altogether. These issues and MRs will be discussed by the cppTango team in their weekly meetings and on Tango Control's Slack channels. We are pretty good in finding a consensus as we are a group of dedicated software developers who have a strong interest in making Tango Controls a good piece of software with a solid foundation.

Very very rarely it happens though, that we disagree and cannot find a common ground. Then Reynald Bourtembourg will have the final say as he is cppTango's long time leader.

# Code guidelines

## Error Handling

There are two categories of errors that need to be handled in cppTango. These
categories can be distinguished by who can handle them and thus who they should
be reported too.

1. Runtime errors -- the function failed to do what was advertised. To be
   reported and handled by the calling code.  The preferred mechanism for reporting
   these errors in cppTango is exceptions.  These are automatically
   propagated over the network to calling processes by CORBA.
2. Program bugs -- the program has reached a state that the code does not
   expect.  This cannot be recovered from programmatically so should be
   reported to a human by crashing the program via assertions.

### Exceptions
Exceptions should be used to raise runtime errors which are to be handled by the
calling code.  Examples:
- A user passed an invalid argument
- A file does not exist
- A connection timed out

In most cases "handling the exception" might just mean reporting to the user
that their request was invalid, or that something went wrong.  This has still
been handled by the code.

#### Guidelines

- Prefer reporting exceptions using the `TANGO_THROW_DETAILED_EXCEPTION` macro with
an appropriate `ExceptionClass`.  If no such appropriate `ExceptionClass` exists
use the `TANGO_THROW_EXCEPTION` macro, which will through an exception of the
`Tango::DevFailed` base class.
- Document what exceptions a function can throw and under what circumstances
- Mark functions which should never throw exceptions as `noexcept` (this
effectively asserts that the function doesn't throw and thus should be thought
of accordingly).
- When calling a function which can throw make sure your function provides, at
least, a weak exception guarantee.  If possible provide a strong exception
guarantee.  Document what exception safety guarantee the function provides,
this might depend on `ExceptionClass` thrown.
- Exceptions will be propagated to other processes by CORBA, this is the
mechanism used to inform the user that there request is invalid. With this in
mind, provide a `desc` to help the user understand what when wrong.  Here are
some prompts for what information to include in your description:
    + What happened?
    + What are the current circumstances? Provide the reason for this exception.
    + What was expected to happen? Tell what the expected and normal situation is.
    + If a value was involved:
        * What was the expected value?
        * What was the received value?
    + What will the consequence of the abnormal situation be?
    + Describe what will not be normal from now on.
    + What will happen now?
    + Will the current execution be aborted or
    + Will the current execution continue?
- Consider catching exceptions just to add more contextual information for the
user.  Use `TANGO_RETHROW_DETAILED_EXCEPTION`/`TANGO_RETHROW_EXCEPTION` to rethrow
the exception.

### Assertions
Assertions are checks in the code for situations that it is not prepared to
handle.  Add assertions in the program to guard against bugs by reporting the
problem as soon as it is detected. Remember that this reporting is done by
crashing the program.

A common place for invalid assumptions about the state of the program to occur
is on the function call boundary.  This is because a function might have a
"preconditions" which the author of the calling code is unaware of, e.g. a
function expects non-null pointer arguments.  Authors of internal functions
should use assertions to guard against these potential miss understandings.

#### Guidelines

- External functions should have wide contracts (i.e. no preconditions).  Raise
an exception if called with invalid arguments.
- For internal functions with narrow contracts (i.e. with preconditions):
    + Document a functions preconditions and assert them at the start of the
    function with `TANGO_ASSERT`
    + If a function has a non-trivial precondition, provide a predicate function to
    check it
- Assert assumptions in the code using the `TANGO_ASSERT` macro to document your
understanding of the program state at that point

# Glossary

- [co]:[Code Owners](https://gitlab.com/tango-controls/cppTango/-/blob/main/CODEOWNERS): The cppTango contributors who have a little bit more decision power than contributors. Usually they are the people who are either paid for working on cppTango or Tango Controls or they can just spend a significant amount of their time on cppTango or Tango Controls.
