Version 2 (modified by 14 years ago) ( diff ) | ,
---|
Please be free to improve this page directly or post on the Boost mailing list.
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 cannot be considered as a bug while the same undocumented modification could be a bug.
The key issues 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 also contains some guidelines related to the code itself.
But, why can user code 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 that the addition of 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 shouldn't have user code breaking if 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 behave as before. The 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 his role to play. Note that the author plays also the role of user of all libraries on which his library depends and on his own library when writing the library tests.
Deprecating features
To be added
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 a 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 possible incompatibilities to the user.
Create a Feature Request ticket for each deprecated/suppressed/modified/new feature [author, user]
Each modification should be tracked with a ticket on the trac system.
Release notes and trac system traceability [author]
The release notes should include
- the list of all the bug tickets that have been solved
- the list of all deprecated/suppressed/modified/new features with their associated tickets.
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).
Coding
Do not use using directives [user]
As seen before, using directives in user code is one of the sources of user code breaking, so it will be safer to use namespace synonyms instead 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 his 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_WARNS_DEPRECATED) || defined(BOOST_WARNS_DEPRECATED_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_WARNS_DEPRECATED and BOOST_WARNS_DEPRCEATED_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 deleting a class on the next version protect its definition by BOOST_DONT_INCLUDE_DEPRECATED or BOOST_LIBX_DONT_INCLUDE_DEPRECATED.
// 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) #define BOOST_LIBX_NAME_DONT_DEFINED #else #warning "name is deprecated, please use new_name instead" class name { // as before }; #endif } }
The same applies to functions and variables.
Do not modify functions prototype prematurely [author]
Instead of modifying an existing function prototype, do as if you were deleting it and adding the new one. As both prototypes should coexist for some releases, check if this overloading could cause user code breaks.
Test and Inspections
Regression Test and trac system traceability
A bug ticket must be associated to a test case, either the test case exists already and there is a regression for some toolset, or a new toolset is considered, or a new test case is needed to check the bug is there (problem reproducible) and the modification solve the problem.
- State clearly which test cases prove the bug it it exists [user]
- Propose a new test case reproducing the bug [user]
Preserve the test from the preceding versions as much as possible
The tests from the preceding versions should not be changed when the author modifies its library. These unchanged tests are a probe of compatibility with the preceding version. When these tests 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 and remove these tests from the Jamfile.