wiki:Guidelines/MaintenanceGuidelines

Version 4 (modified by viboes, 14 years ago) ( diff )

--

WARNING: The contents of this page is not the result of a consensus on the Boost community.

Please be free to improve this page directly or post on the Boost mailing list boost-AT-lists.boost.org and boost-users-AT-lists.boost.org.

Motivation

To be added

Introduction

Nobody wants to completely disallow changes, we need them. Everybody wants to avoid breaking changes, but sometimes these are also necessary.

This page describes guidelines that could be followed by the Boost community to avoid user code breaking when the user upgrades to a more recent Boost release.

The difference between breaking changes and bugs concerns documentation. A documented modification breaking user code can not be considered as a bug while the same non documented modification could be a bug.

The key issue are:

  • Documenting and tracking the changes.
  • Avoiding completely silent breakage by a deep inspection of code changes.
  • Using a deprecation period to give users notice that a breaking change is coming.
  • Running the regression tests from the _previous_ major release against the current release as a way to automatically detect and flag interface/behavioral changes.
  • Versioning individual Boost.Libraries.

These guidelines are related mainly to how to document changes. There are also some guidelines that can be followed to detect breaking changes either by test or by inspections. And of course this pages contains also some guidelines related to the code itself.

But, why user code can break when upgrading Boost?

  • Syntactical breaking: detected at compile time

It is evident that the removal of files, namespaces, classes, function, variables or macros could break user code. What it is less evident is the addition namespaces, classes, function, variables at the namespace level or macros can also break user code. Note that modifications can be considered as deletion+addition.

The following user code example

using namespace boost;

class foo {
  // ...
};

void bar() {
  // ...
}

breaks when

  • we add the namespace foo in one of the files included by the user program
    // boost/file.hpp
    namespace boost {
      namespace foo {
        // ...
      }
    }
    
  • we add the class foo in one of the files included by the user program
    // boost/file.hpp
    namespace boost {
      class foo {
        // ...
      };
    }
    
  • we add the function bar in one of the files included by the user program
    // boost/file.hpp
    namespace boost {
      void bar() {
        // ...
      }
    }
    

Adding macros We should don't have user code breaking until we preserve the macro naming rule BOOST_LIBNAME_NAME.

  • Semantical breaking: detected at runtime

Some times the user code will compile with the upgraded Boost release, but the code will not behaves as before. This kind of changes that could lead to this situation must be over documented because the compiler can not help the user to catch the breaking code.

Add an example on overloading

These guidelines are not only addressed to the Boost library authors, but also to the users and the release manager team (RM). Every member of the Boost community has its role to play. Note that the author plays also the role of user of all libraries on which its library depends and on its own library when writing the library tests.

Deprecating features

C++ do not have deprecated attributes, so the author needs to emulate a warning when some deprecated feature is used. The #warning directive can be used to this purpose

  #warning "name is deprecated, please use new_name instead"

The main problem is that this warning will appear independently of whether the user code uses or not the deprecated feature. In order to palliate to this the inclusion of the deprecated feature could be accessible conditionally and only when included the warning will be reported or not.

For compatibility purposes deprecated features should be included by default. So the library behaves as if the features have not been deprecated. If a user don't want to include a deprecated feature he/she can define one of the following macros:

  • BOOST_DONT_INCLUDE_DEPRECATED: don't include any deprecated feature.
  • BOOST_DONT_INCLUDE_DEPRECATED_ON_X_Y: don't include any deprecated feature to be removed on version X.Y.
  • BOOST_LIBX_DONT_INCLUDE_DEPRECATED: don't include any deprecated feature for library Boost.LIBX.
  • BOOST_LIBX_DONT_INCLUDE_DEPRECATED_ON_X_Y: don't include any deprecated feature for libarary Boost.LIBX to be removed on version X.Y.
  • BOOST_LIBX_DONT_INCLUDE_DEPRECATED_NAME: don't include the deprecated feature name.

Once one of this macros is defined the author can define another macro to make easier the work.

  • BOOST_LIBX_NAME_DECLARED: states that the deprecated feature name has been declared

Deprecated warning are not reported by default, so the library behaves as if the features have not been deprecated. If the user wants this deprecated warnings to appear he/she can define one of the macros:

  • BOOST_WARMS_DEPRECEATED: warms on all deprecated features
  • BOOST_WARMS_DEPRECEATED_ON_X_Y: warms on all deprecated features to be removed on version X.Y

See the examples below.

Cross version testing

Running the regression tests from the _previous_ major release against the current release is a way to automatically detect and flag interface/behavioral changes. Sure, there are many potential problems with such an approach:

  • Some library tests are likely to also exercise code at the implementation/detail level.
  • Already limited testing resources (on the other hand, interface changes are likely to be detected by running such tests for a single, reasonably compliant, compiler).
  • ...

In order to have the better we can preserve the test of the older releases and run them on the current release.

Versioning individual Boost libraries

This page do not contains any advice related to individual Boost libraries packaging. There is already the CMake project working on that. But tagging individual Boost libraries with a specific version has already some advantages.

Documentation

Tag Boost library with specific library version [author]

Tagging each library release with a version, is the better way to signal to the user of possible incompatibilities.

For example the thread library could have:

  • 2.1.0 Changes since boost 1.35
  • 2.0.0 Changes since boost 1.34
  • 1.0.0 Initial release on 1.25

Create a Feature Request ticket for each deprecated/suppressed/modified/new feature [author, user]

Each modification should be tracked with a ticket on the track system.

Release note and track system traceability [author]

The release note should include

  • the list of all the bug tickets that have been solved
  • the list of all deprecated/suppressed/modified/new features with its associated ticket.

For example the thread library could have (#XXXX should be replaced by the trac tickets):

  • 2.1.0 Changes since boost 1.35
    • #XXXX: New generic lock() and try_lock() functions for locking multiple mutexes at once.
    • #XXXX: Rvalue reference support for move semantics where the compilers supports it.
    • A few bugs fixed and missing functions added (including the serious win32 condition variable bug).
      • #XXXX: bug1
      • ...
      • #XXXX: bugn
    • #XXXX: scoped_try_lock types are now backwards-compatible with Boost 1.34.0 and previous releases.
    • #XXXX: Support for passing function arguments to the thread function by supplying additional arguments to the boost::thread constructor.
    • #XXXX: Backwards-compatibility overloads added for timed_lock and timed_wait functions to allow use of xtime for timeouts.

Include a diff file showing the modification respect to the previous release [author, RM]

This file is the explicit report of all the changes in the library. It could be used by the Boost community to inspect that every change has been correctly documented (see inspections bellow).

Trac ticket => test cases association [author]

Each feature request or bug should have associated at least a test case. A table showing this association will help to check that each feature,bug is has the expected test coverage.

TicketSynopsisTest Case
#XXXXfoo foo foo bar bar bar test_foo_bar

Codding

Do not use using sentences [user]

As seen before the use of using sentences in user code is one of the source of user code breaking, so it will be safer to use instead namespace synonyms and prefix each symbol with them.

Instead of doing

   using namespace boost::interprocess;

   shared_memory_object::remove("MySharedMemory");

do

    namespace bip = boost::interprocess;

    bip::shared_memory_object::remove("MySharedMemory");

Do not delete files prematurely [author]

Before deleting a file "file.hpp" deprecate it and let the user modify its code during some versions (e.g. until 1_40). Replace it by

// boost/old_header_file.hpp
// include whatever file is needed to preserve the old file contents
#if defined(BOOST_WARMS_DEPRECEATED) || defined(BOOST_WARMS_DEPRECEATED_ON_1_40)
#warning "boost/old_header_file.hpp is deprecated, please include boost/new_header_file.hpp instead
#endif

It will be up to the user to define the macros BOOST_WARMS_DEPRECEATED and BOOST_WARMS_DEPRECEATED_ON_1_40 when the user wants to be warmed for deprecated features on Boost or on the Boost version 1.40 respectively.

Do not delete namespaces prematurely [author]

Use the using sentence to import from the new namespace to the old one.

Do not delete classes/functions/variables prematurely [author]

Instead of delete a class or a public/protected function/variable on the next version protect its declaration by any of the DONT_INCLUDE_DEPRECATED macros.

// boost/libx/header_file.hpp
namespace boost {
namespace libx {

#if defined(BOOST_DONT_INCLUDE_DEPRECATED) || defined(BOOST_DONT_INCLUDE_DEPRECATED_ON_1_41) \
 || defined(BOOST_LIBX_DONT_INCLUDE_DEPRECATED)  || defined(BOOST_LIBX_DONT_INCLUDE_DEPRECATED_ON_1_41) \
 || defined(BOOST_LIBX_DONT_INCLUDE_DEPRECATED_NAME)
#else
  #define BOOST_LIBX_NAME_DECLARED
  #if defined(BOOST_WARMS_DEPRECEATED) || defined(BOOST_WARMS_DEPRECEATED_ON_1_40)
    #warning "name is deprecated, please use new_name instead"
  #endif

  class name {
    name::name();
    // as before
  };
#endif

}
}

When the declaration should been included, the author could define a BOOST_LIBX_NAME_DECLARED, which could be used on the class/function/variable definition. E.g.

// boost/libx/header_file.cpp
namespace boost {
namespace libx {

#if defined(BOOST_LIBX_NAME_DECLARED)
  name::name() {
    // as before
  }
  // as before
#endif

}
}

Do not modify functions prototypes prematurely [author]

Instead of modifying an existing function prototype, do as you were deleted it and added the new one. As both prototypes should cohabit during some releases, check if this overloading could cause user code breaks.

Remove the deprecated features on a given release [author]

Once the deprecated period is expired the author should remove them.

Test and Inspections

Regression Test and track system traceability

A bug ticket should be associated to one or several test cases, either the test cases exists already and we are in face of a regression for some toolset, or a new toolset is considered, or a new test case is needed to reproduce the bug and the modification solve the problem.

  • State clearly which test cases reproduce the bug it they exists [user]
  • Propose a new test case reproducing the bug otherwise [user]

Preserve the test from the preceding versions as much as possible [author]

The test from the preceding versions should not be changed when the author modifies its library. These not changed tests are a probe of compatibility with the preceding version. When these test must be changed they indicate a breaking user code issue. So instead of changing them add new ones stating explicitly on which version they were included. Old tests that should fail can be moved to the compile_fail or run_fail tests on the Jamfile.

Release Management

Announce new features on a library [author]

It would be great if the author announce to the Boost mailing lists the new features just before committing in the release branch. This will let the time to the Boost community to inspect the modifications introduced.

Inspect announced modifications match the real ones [author, user, RM]

The use of the diff file will help to inspect that the modifications introduced have been correctly included on the documentation and a good test coverage has been done. This will result in a non official mini-review of the library.

Note: See TracWiki for help on using the wiki.