Development Guidelines

The following guidelines are used for Orekit development.

Development

Orekit is a low level library. It may be used in very different contexts which cannot be foreseen, from quick studies up to critical operations. The main driving goals are the following ones:

  • validation

  • robustness

  • maintainability

  • efficiency

The first goal, validation, implies tests must be as extensive as possible. They should include realistic operational cases but also contingency cases. The jacoco tool must be used to monitor test coverage. A very high level of coverage is desired. We do not set up mandatory objective figures, but only guidelines here. However,a 60% line coverage would clearly not be acceptable at all and 80% would be considered deceptive.

The second goal, robustness, has some specific implications for a low level component like Orekit. In some sense, it can be considered an extension of the previous goal as it can also be improved by testing. It can also be improved by automatic checking tools that analyze either source code or binary code. The findbugs tool is already configured for automatic checks of the library using a maven plugin.

This is however not sufficient. A library is intended to be used by applications unknown to the library development team. The library development should be as flexible as possible to be merged into an environment with specific constraints. For example, perhaps an application should run 24/7 during months or years, so caching all results to improve efficiency may prove disastrous in such a use case, or it should be embedded in a server application, so printing to standard output should never be done. Experience has shown that developing libraries is more difficult than developing high level applications where most of the constraints are known beforehand.

The third goal, maintainability, implies code must be readable, clear and well documented. Part of this goal is enforced by the stylistic rules explained in the next section, but this is only for the automatic and simple checks. It is important to keep a clean and extensible design. Achieving simplicity is really hard, so this goal should not be taken too lightly. Good designs are a matter of balance between two few objects that do too many things internally an ignore each other and too many objects that do nothing alone and always need a bunch of other objects to work. Always think in terms of balance, and check what happens if you remove something from the design. Quite often, removing something improves the design and should be done.

The fourth goal, efficiency, should be handled with care to not conflict with the second and third goals (robustness and maintainability). Efficiency is necessary but trying too much too achieve it can lead to overly complex unmaintainable code, to too specific fragile code, and unfortunately too often without any gain after all because of premature optimization and unfounded second-guess.

One surprising trick, that at first sight might seem strange and inappropriate has been used in many part for Orekit and should be considered a core guideline. It is the use of immutable objects. This trick improves efficiency because many costly copying operation are avoided, even unneeded one added for defensive programming. It improves maintainability because both the classes themselves and the classes that use them are much simpler. It also improves robustness because many (really many ...) difficult to catch bugs are caused by mutable objects that are changed in some deeply buried code and have an impact on user code that forgot to perform a defensive copy. Orbits, dates, vectors, and rotations are all immutable objects.

Source Control Management

The source code control management system used is Git. The main Orekit repository is located at https://www.orekit.org/git/orekit-main.git.

Starting after version 9.0, the branch management workflow is adapted from both git flow and from the Orfeo ToolBox Workflow.

Orekit git workflow

This implies that development occurs on a develop branch only. Developers create feature branches, and merge them on the develop branch when ready. The develop branch should always be functional so people wanting to be on the bleeding edge can use it (for example to create nightly builds or to prepare their application for upcoming features before the official release).

In order to improve traceability, when a feature branch is merged into develop, it should use

git merge ---no-ff

the --no-ff flag prevents fast-forward so the history makes it clear there was a feature branch at this point.

Feature branches are temporary, they are deleted when they are not useful anymore. Feature branches may be present only in one developer workspace and never appear in the main repository, however, they may also be pushed on the main repository if the developer wants to share work or needs community feedback.

When a release is desired, a dedicated branch should be created, with a name following the pattern release-x.y. These branches are created from the develop branch. When the release is ready, the branch is merged both into the master branch and into the develop branch. Once a release branch has been set up, it will remain. This allows users relying on this specific version to be able to retrieve the fixes published afterward (i.e. x.y.1, x.y.2...).

After a release has been published and pushed to master, it may be necessary to publish an urgent bugfix if a serious problem in the released version is found. Short-lived bugfix branches that are created directly from master are devoted to this. These bugfix branches are merged back into master and develop.

The master branch always refer to the latest stable release performed. No direct work is done on this branch. It is updated only by merging either release branches or bugfix branches branches to it.

Style Rules

For reading ease and consistency, the existing code style should be preserved for all new developments. The rules are common ones, inherited mainly from the Sun Code Conventions for the Java Programming Language guide style and from the default checkstyle tool configuration. A few of these rules are displayed below. The complete definition is given by the checkstyle configuration file in the project root directory.

  • header rule

    all source files start with the Apache license header,

  • indentation rules

    no tabs, 4 spaces indentation, no indentation for case statements,

  • operators wrapping rules

    lines are wrapped after operators (unlike Sun),

  • whitespace rules

    operators are surrounded by spaces, method parameters open parenthesis is not preceded by space, lines do not end with white space,

  • curly brace rules

    open curly brace are at end of line, with the matching closing curly brace aligned with the start of the corresponding keyword (if, for, while, case or do),

  • encoding rules

    characters encoding is UTF8, the git property core.autocrlf should be set to input on Linux development machines and to true on Windows development machines (to ensure proper conversion on all operating systems),

  • naming rules

    classes names begin with upper case, instance methods and fields names begin with lower case, class fields are all upper case with words separated by underscores,

  • ordering rules

    class variables come first, followed by instance variables, followed by constructors, and followed by methods, public modifiers come first, followed by protected modifiers followed by private modifiers,

  • javadoc rules

    all elements have complete javadoc, even private fields and methods (there are some rare exceptions, in case of code translated from the fortran language and models with huge parameters sets),

  • robustness rules

    switch/case construct have a default argument, even when all possible cases are already handled, as many classes as possible are immutable,

  • miscellaneous rules

    star imports are forbidden, parameters and local variables are final wherever possible.

Design Rules

  • coverage (validation)

    seek for a line test coverage of at least 80% (more is better)

  • findbugs (robustness)

    fix all errors and warnings found by findbugs

  • no runtime assumptions (robustness)

    do not make assumptions on the runtime environment of applications using Orekit (they may be embedded with no console, no possible user interaction, no network, no writable file system, no stoppable main program, have memory constraints, time constraints, be run in different linguistic contexts ...)

  • simplicity (maintainability)

    follow Occam's razor principle or its declination in computer science: KISS (Keep It Simple, Stupid)

  • balanced design (efficiency)

    seek efficiency, but do not overstep robustness and maintainability

  • immutable objects (robustness, maintainability)

    use immutable objects as much as possible

  • checkstyle (style)

    fix all errors and warnings found by checkstyle

orekit-git-flow.png - Orekit Git workflow (61.3 KB) Luc Maisonobe, 2017-08-06 21:01