16 | | 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'''. |
17 | | |
18 | | 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. |
19 | | |
20 | | The key issue are: |
21 | | * Documenting and tracking the changes. |
22 | | * Avoiding completely silent breakage by a deep inspection of code changes. |
23 | | * Using a deprecation period to give users notice that a breaking change is coming. |
24 | | * Running the regression tests from the _previous_ major release against the current release as a way to automatically detect and flag interface/behavioral changes. |
25 | | * Versioning individual Boost.Libraries. |
26 | | |
27 | | 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. |
28 | | |
29 | | But, why user code can break when upgrading Boost? |
| 19 | This page describes guidelines that could help the Boost community to get stable libraries and as consequence '''to avoid user code breaking when the user upgrades to a more recent Boost release'''. |
| 20 | |
| 21 | 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. |
| 22 | |
| 23 | ----------------------------------------------------------------------------------------------- |
| 24 | == User code breaking cases == |
| 25 | But why can user code break when the user upgrades to a more recent Boost release. |
| 26 | While this do not pretends to be exhaustive, some examples are given. |
32 | | 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. |
33 | | |
34 | | The following user code example |
35 | | |
36 | | {{{ |
37 | | #!cpp |
| 29 | 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, functions, variables at the namespace level or macros can also break user code. Note that |
| 30 | modifications can be considered as deletion+addition. |
| 31 | |
| 32 | The following code example |
| 33 | |
| 34 | {{{ |
| 35 | #!cpp |
| 36 | #include <boost/file.hpp> |
90 | | 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. |
91 | | |
92 | | ''Add an example on overloading'' |
93 | | |
94 | | 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. |
95 | | |
96 | | |
97 | | = Deprecating features = |
98 | | |
99 | | C++ do not have deprecated attributes, so the author needs to emulate a warning when some deprecated feature is used. |
100 | | The #warning directive can be used to this purpose |
101 | | |
| 88 | 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. |
| 89 | |
| 90 | The following code example |
| 91 | |
| 92 | {{{ |
| 93 | #!cpp |
| 94 | #include <boost/file.hpp> |
| 95 | using namespace boost; |
| 96 | |
| 97 | |
| 98 | void bar(int i) { |
| 99 | // ... |
| 100 | } |
| 101 | }}} |
| 102 | |
| 103 | breaks when |
| 104 | * we add the function bar in one of the files included by the user program |
| 105 | {{{ |
| 106 | #!cpp |
| 107 | // boost/file.hpp |
| 108 | namespace boost { |
| 109 | void bar(char c) { |
| 110 | // ... |
| 111 | } |
| 112 | } |
| 113 | }}} |
| 114 | |
| 115 | |
| 116 | These guidelines are not only addressed to the Boost library developers, 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 library depends and on its own library when writing the library tests. |
| 117 | |
| 118 | |
| 119 | The key issue to get stable libraries are: |
| 120 | * Versioning individual Boost.Libraries. |
| 121 | * Using a deprecation period to give users notice that a breaking change is coming. |
| 122 | * Documenting and tracking the changes. |
| 123 | * Avoiding completely silent breakage by a deep inspection of code changes and running the regression tests (functional tests) from the _previous_ major release against the current release as a way to automatically detect and flag interface/behavioral changes. |
| 124 | |
| 125 | These guidelines are related mainly to how to document changes, how breaking changes can be |
| 126 | detected either by test or by inspections, and of course guidelines related to the code itself. |
| 127 | |
| 128 | |
| 129 | ----------------------------------------------------------------------------------------------- |
| 130 | == Versioning individual Boost libraries == |
| 131 | |
| 132 | This page don't 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 MAJOR.MINOR.PATCH has already the informational advantage. |
| 133 | |
| 134 | Versions are denoted using a standard triplet of integers: MAJOR.MINOR.PATCH. The basic intent is that MAJOR versions are incompatible, large-scale upgrades of the API. MINOR versions retain source and binary compatibility with older minor versions, and changes in the PATCH level are perfectly compatible, forwards and backwards. |
| 135 | |
| 136 | |
| 137 | ----------------------------------------------------------------------------------------------- |
| 138 | == Deprecating features == |
| 139 | |
| 140 | C++ don't have deprecated attributes, so the developer needs to emulate a warning when some deprecated feature is used. The #warning directive can be used to this purpose. |
167 | | == Feature Request for each deprecated/suppressed/modified/new feature [author, user] == #feature_request |
168 | | Each modification should be tracked with a ticket on the track system. |
169 | | |
170 | | == Release note and track system traceability [author] == #release_note |
171 | | The release note should include |
| 201 | ----------------------------------------------------------------------------------------------- |
| 202 | == Make feature request for each feature [developer, user] == #feature_request |
| 203 | Each modification deprecated/suppressed/modified/new feature should be tracked with a ticket on the trac system. |
| 204 | |
| 205 | ----------------------------------------------------------------------------------------------- |
| 206 | == Include the tracked tickets on the Release notes [developer] == #release_note |
| 207 | The release notes should include |
196 | | == Dependency to other Boost library [author] == #dependency |
197 | | |
198 | | |
199 | | = Codding = |
200 | | == Do not use using sentences [user] ==#dont_using |
201 | | 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. |
| 233 | ----------------------------------------------------------------------------------------------- |
| 234 | == List the dependency upon other Boost library [developer] == #dependency |
| 235 | Each library lists the other libraries it depends upon and the minimum |
| 236 | version # - as it does with compilers now |
| 237 | |
| 238 | ||'''Library'''||'''Version #'''||'''Build & Link'''||'''Comment'''|| |
| 239 | ||Thread||2.1.0||link||generic lock()|| |
| 240 | |
| 241 | ----------------------------------------------------------------------------------------------- |
| 242 | == Document behavior differences between release and debug variants [developer] == #debug |
| 243 | Document the differences in behavior that depends on the user defines, in particular the release and debug variants. |
| 244 | |
| 245 | |
| 246 | ----------------------------------------------------------------------------------------------- |
| 247 | == Document behavior differences between toolsets [developer] == #toolsets |
| 248 | Document the differences in behavior that depends on the toolsets. |
| 249 | |
| 250 | |
| 251 | ----------------------------------------------------------------------------------------------- |
| 252 | = Coding = |
| 253 | |
| 254 | |
| 255 | ----------------------------------------------------------------------------------------------- |
| 256 | == Don't use using directives [user] ==#dont_using |
| 257 | 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. |
222 | | |
223 | | == Do not delete files prematurely [author] == #dont_delete_files |
224 | | |
225 | | 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 |
| 278 | ----------------------------------------------------------------------------------------------- |
| 279 | == Avoid the use of include all features files at the /boost level [user] == |
| 280 | Avoid the use of include all features files at the /boost level use instead specific files at boost/lib/file.hpp. Including less code reduce your user code breaking chances. |
| 281 | |
| 282 | ----------------------------------------------------------------------------------------------- |
| 283 | == Be careful with the use of using namespace header files [developer] == |
| 284 | To import symbols from another namespace the developer can use the using directive '''using namespace''' but limited to the function level. |
| 285 | |
| 286 | ----------------------------------------------------------------------------------------------- |
| 287 | == Don't overload functions that are used by the TR1 (using using)[developer] == |
| 288 | Overloading imported Boost.TR1 functions is equivalent to overload the std functions. |
| 289 | |
| 290 | ----------------------------------------------------------------------------------------------- |
| 291 | == Avoid include-all-features files at the /boost level == |
| 292 | Avoid include-all-file at the /boost level including all the functionalities of the library. Provide instead specific files at boost/lib/file.hpp. |
| 293 | |
| 294 | If you provide such files ensure that your tests works when you include them directly or include the specific ones. |
| 295 | |
| 296 | ----------------------------------------------------------------------------------------------- |
| 297 | == Don't refine functions overloading without ensuring the same behavior == |
| 298 | |
| 299 | If you need document them and warm the user. |
| 300 | |
| 301 | ----------------------------------------------------------------------------------------------- |
| 302 | == Avoid the inclusion of symbols at the boost or boost::detail namespace == |
| 303 | These symbols can collide with symbols of other libraries. |
| 304 | |
| 305 | Announce them on the Boost mailing list when you think this is the best choice. |
| 306 | |
| 307 | ----------------------------------------------------------------------------------------------- |
| 308 | == Avoid different external behavior depending on the variant release or debug without documenting it == |
| 309 | |
| 310 | Providing different external behavior depending on the variant release or debug could be a surprise for the user. |
| 311 | |
| 312 | If you need them document it and warm the user. |
| 313 | |
| 314 | ----------------------------------------------------------------------------------------------- |
| 315 | == Avoid to change interfaces [developer] == #dont_change_interfaces |
| 316 | When you change the interface, you can see the changes that are needed at the Boost level, but you can not evaluate the cost induced by this change for the users. So before to change an interface deprecate it and let the user enough time to migrate to the new one. |
| 317 | |
| 318 | ----------------------------------------------------------------------------------------------- |
| 319 | === Don't delete files prematurely [developer] === #dont_delete_files |
| 320 | |
| 321 | 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 |
288 | | == Do not modify functions prototypes prematurely [author] == #dont_modify_prototypes |
289 | | 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. |
290 | | |
291 | | == Remove the deprecated features on a given release [author] == #deprecated_removal |
292 | | Once the deprecated period is expired the author should remove them. |
293 | | |
| 386 | ----------------------------------------------------------------------------------------------- |
| 387 | === Don't modify functions prototypes prematurely [developer] === #dont_modify_prototypes |
| 388 | Modifying an existing function prototype, if you were deleting it and adding the new one. Instead add the new when possible. |
| 389 | |
| 390 | As both prototypes should coexist for some releases, check if this overloading could cause user code breaks. |
| 391 | |
| 392 | ----------------------------------------------------------------------------------------------- |
| 393 | === Remove the deprecated features on a given release [developer] === #deprecated_removal |
| 394 | Once the deprecated period is expired the developer sould remove them. |
| 395 | |
| 396 | Don't forget to change the major version number. |
| 397 | |
| 398 | ----------------------------------------------------------------------------------------------- |
295 | | |
296 | | == Regression Test and track system traceability == #ticket_test_case [user] |
| 400 | ----------------------------------------------------------------------------------------------- |
| 401 | == Test headers [developer] == |
| 402 | Steve Watanave has writen a Jamfile that make easier this task. See /boost/libs/units/test/test_header for an example. |
| 403 | |
| 404 | === Include each header files twice [developer] === |
| 405 | The inclusion of a header file twice ensure that the file is self contained, and that it is well protected |
| 406 | |
| 407 | {{{ |
| 408 | #!cpp |
| 409 | #include <file.hpp> |
| 410 | #include <file.hpp> |
| 411 | int main() {return 0;} |
| 412 | }}} |
| 413 | |
| 414 | |
| 415 | ----------------------------------------------------------------------------------------------- |
| 416 | === include each couple of header files in both orders === |
| 417 | The inclusion of two header file ensure that there are no duplicates declarations on the two files. |
| 418 | |
| 419 | Currently there are no tests inter-library. It would be a good idea to start by checking that any header file can be include with any other file. |
| 420 | |
| 421 | Note that when file1 and file 2 and the same this correspond to the preceding test. |
| 422 | |
| 423 | {{{ |
| 424 | #!cpp |
| 425 | #include <file1.hpp> |
| 426 | #include <file2.hpp> |
| 427 | }}} |
| 428 | |
| 429 | {{{ |
| 430 | #!cpp |
| 431 | #include <file2.hpp> |
| 432 | #include <file1.hpp> |
| 433 | }}} |
| 434 | |
| 435 | Note - It will be interesting to be able to get this automatically. |
| 436 | ----------------------------------------------------------------------------------------------- |
| 437 | === Include all header files === |
| 438 | One more test could be useful, include all the Boost Headers files |
| 439 | {{{ |
| 440 | #!cpp |
| 441 | #include <file1.hpp> |
| 442 | #include <file2.hpp> |
| 443 | ... |
| 444 | #include <filen.hpp> |
| 445 | }}} |
| 446 | |
| 447 | ----------------------------------------------------------------------------------------------- |
| 448 | === link all the header files twice === |
| 449 | This ought to catch non-inlined functions and other duplicate definitions |
| 450 | |
| 451 | ----------------------------------------------------------------------------------------------- |
| 452 | == Don't forget to test == |
| 453 | ----------------------------------------------------------------------------------------------- |
| 454 | === The implicitly generated member functions === |
| 455 | The implicitly generated member functions are part of your interface. |
| 456 | |
| 457 | Add some simple tests that prove its correct behavior. |
| 458 | |
| 459 | ----------------------------------------------------------------------------------------------- |
| 460 | === The removed default member functions when you declare a constructor === |
| 461 | When you declare a constructor the other default constructors are not generated, so |
| 462 | |
| 463 | ----------------------------------------------------------------------------------------------- |
| 464 | === The deleted (private) default member functions === |
| 465 | The default member functions =deleted (or declared private) are not part of the interface. |
| 466 | |
| 467 | Add some simple tests that prove the compilation fails when you use them. |
| 468 | |
| 469 | ----------------------------------------------------------------------------------------------- |
| 470 | === The explicit constructors === |
| 471 | |
| 472 | ----------------------------------------------------------------------------------------------- |
| 473 | === The implicit constructors or conversions === |
| 474 | |
| 475 | ----------------------------------------------------------------------------------------------- |
| 476 | === The const-ness of variables, function parameters and function return types and functions === |
| 477 | When a variable, function parameter or function return type is non-const, test that you can modify them. |
| 478 | |
| 479 | When a variable, function parameter or function return type is const, test that you try to modify them the compilation fails. |
| 480 | |
| 481 | ----------------------------------------------------------------------------------------------- |
| 482 | == Separate the functional test from the unit test (implementation test) == |
| 483 | Functional test should take in account only the documented behavior. They can be used as regression test for new re-factorings of the implementation. |
| 484 | |
| 485 | Implementation test can be used to test details in the implementation but not as regression tests. |
| 486 | |
| 487 | ----------------------------------------------------------------------------------------------- |
| 488 | == Track regression test on the trac system tickets == #ticket_test_case [user] |
300 | | == Preserve the test from the preceding versions [author] == #released_tests |
301 | | 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. |
302 | | |
303 | | == Set read-only right on the released test files [author] == #read_only_released_tests |
304 | | In order to avoid erroneous modification of the the released tests files the author could change the rights of the test files. If the author has no permission to do that as a the last resort he could change the permission in its own SVN copy. |
305 | | |
306 | | |
| 492 | ----------------------------------------------------------------------------------------------- |
| 493 | == Preserve the functional test from the preceding versions [developer] == #released_tests |
| 494 | While doing modifications to the library the developer can find some regressions on some tests of the preceding versions. It seems natural to change them to make the test succeed. This is not a good idea. The failing test is a symptom that the new modifications could break user code. |
| 495 | |
| 496 | If the modification don't pretended to be an interface break, the developer should modify the library until the preceding test cases pass. |
| 497 | |
| 498 | If the break is intentional move the tests to the failing tests compile_fail or run_fail on the Jamfile. The developer can copy the test and adapt it to the new interface. It could be a good idea to state explicitly on the test file name on which version they were included. |
| 499 | |
| 500 | In order to avoid erroneous modification of the the released tests files the developer could change the rights of the test files. If the developer has no permission to do that on the SVN repository as a last resort he could change the permission in its own SVN copy. |
| 501 | |
| 502 | ----------------------------------------------------------------------------------------------- |
308 | | == Announce new library features [author] == #announce |
309 | | 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. |
310 | | |
311 | | == Inspect the code [author, user, RM] == #review |
312 | | Inspect the code to check that the new modifications do not include user code breaks. Tools comparing the contents of the current release and the preceding one could be help. |
313 | | |
314 | | == Check that every modification has been documented [author, user, RM] == #coherent_doc |
315 | | Inspect that every modification has been reported on the documentation. Tools comparing the contents of the current release and the preceding one could be help. |
316 | | |
317 | | == Check that every modification has been tested [author, user, RM] == #test_coverage |
318 | | Inspect that every modification has been tested. Tools comparing the contents of the current release and the preceding one could be help. |
319 | | |
320 | | |
321 | | = Author guidelines = |
322 | | ||Kind||Guideline|| |
| 504 | ----------------------------------------------------------------------------------------------- |
| 505 | == Announce new library features [developer] == #announce |
| 506 | It would be great if the developer 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. |
| 507 | |
| 508 | ----------------------------------------------------------------------------------------------- |
| 509 | == Inspect the code [developer, user, RM] == #review |
| 510 | Inspect the code to check that the new modifications don't include user code breaks. Tools comparing the contents of the current release and the preceding one could help. |
| 511 | |
| 512 | ----------------------------------------------------------------------------------------------- |
| 513 | == Check that every modification has been documented [developer, user, RM] == #coherent_doc |
| 514 | Inspect that every modification has been reported on the documentation. Tools comparing the contents of the current release and the preceding one could help. |
| 515 | |
| 516 | ----------------------------------------------------------------------------------------------- |
| 517 | == Check that every modification has been tested [developer, user, RM] == #test_coverage |
| 518 | Inspect that every modification has been tested. Tools comparing the contents of the current release and the preceding one could help. |
| 519 | |
| 520 | |
| 521 | ----------------------------------------------------------------------------------------------- |
| 522 | = Developer guidelines = |
| 523 | ||'''Kind'''||'''Guideline'''|| |