|
| 1 | +# Deprecation Policy |
| 2 | + |
| 3 | +Many users and other packages depend on this package. We must |
| 4 | +make sure that whenever we make changes to the code, we give users ample time to |
| 5 | +adjust without breaking code that they have already written. |
| 6 | + |
| 7 | +Most importantly: *do not* change any interface that is public-facing unless we |
| 8 | +absolutely have to. Adding things is ok, taking things away is annoying for |
| 9 | +users but can be handled reasonably with plenty notice, but changing behavior |
| 10 | +generally means users cannot write code that will work with two subsequent |
| 11 | +versions of qiskit-ibm-runtime, which is not acceptable. |
| 12 | + |
| 13 | +Beware that users will often be using functions, classes and methods that we, |
| 14 | +the Qiskit developers, may consider internal or not widely used. Do not make |
| 15 | +assumptions that "this is buried, so nobody will be using it"; if it is public, |
| 16 | +it is subject to the policy. The only exceptions here are functions and modules |
| 17 | +that are explicitly internal, *i.e.* those whose names begin with a leading |
| 18 | +underscore (`_`). |
| 19 | + |
| 20 | +The guiding principles are: |
| 21 | + |
| 22 | +- we must not remove or change code without active warnings for at least three |
| 23 | + months or two complete version cycles; |
| 24 | + |
| 25 | +- there must always be a way to achieve valid goals that does not issue any |
| 26 | + warnings; |
| 27 | + |
| 28 | +- never assume that a function that isn't explicitly internal isn't in use; |
| 29 | + |
| 30 | +- all deprecations, changes and removals are considered API changes, and can |
| 31 | + only occur in minor releases not patch releases, per the [stable branch policy](https://github.com/Qiskit/qiskit/blob/main/MAINTAINING.md#stable-branch-policy). |
| 32 | + |
| 33 | + |
| 34 | +## Removing a feature |
| 35 | + |
| 36 | +When removing a feature (for example a class, function or function parameter), |
| 37 | +we will follow this procedure: |
| 38 | + |
| 39 | +- A deprecation warning must be issued prior to any removal. The warning |
| 40 | + must indicate what the alternative path is, and the alternative path |
| 41 | + must be in place when the warning is issued. When a feature is |
| 42 | + deprecated, add a |
| 43 | + release note with a `deprecations` section listing all deprecated paths, |
| 44 | + their alternatives, and the reason for deprecation. [Update the tests to test the warnings](#testing-deprecated-functionality). |
| 45 | + |
| 46 | + *Reason*: we need to give people time to swap over without breaking their |
| 47 | + code as soon as they upgrade. |
| 48 | + |
| 49 | +- Set a removal date for the old feature, and remove it (and the warnings) when |
| 50 | + reached. This must be at least three months after the version with the |
| 51 | + warnings was first released, and cannot be the minor version immediately |
| 52 | + after the warnings. Add an `upgrade` release note that lists all the |
| 53 | + removals. For example, if the alternative path was provided |
| 54 | + and the warnings were added in `0.20.0`, the earliest version for removal |
| 55 | + is `0.22.0`, even if `0.21.0` was released more than three months after |
| 56 | + `0.20.0`. |
| 57 | + |
| 58 | + **Note: These are _minimum_** requirements. For removal of significant or core features, give |
| 59 | + users at least an extra minor version if not longer.** |
| 60 | + |
| 61 | + *Reason*: there needs to be time for users to see these messages, and to give |
| 62 | + them time to adjust. Not all users will update their version of qiskit-ibm-runtime |
| 63 | + immediately, and some may skip minor versions. |
| 64 | + |
| 65 | +When a feature is marked as deprecated it is slated for removal, but users |
| 66 | +should still be able to rely on it to work correctly. We consider a feature |
| 67 | +marked "deprecated" as frozen; we commit to maintaining it with critical bug |
| 68 | +fixes until it is removed, but we won't merge new functionality to it. |
| 69 | + |
| 70 | + |
| 71 | +## Changing behavior |
| 72 | + |
| 73 | + |
| 74 | +Changing behavior without a removal is particularly difficult to manage, because |
| 75 | +we need to have both options available for two versions, and be able to issue |
| 76 | +warnings. For example, changing the type of the return value from a function |
| 77 | +will almost invariably involve making an API break, which is frustrating for |
| 78 | +users and makes it difficult for them to use this package. |
| 79 | + |
| 80 | +The best solution here is often to make a new function, and then use [the procedures for removal](#removing-features) above. |
| 81 | + |
| 82 | +If you absolutely must change the behavior of existing code (other than fixing |
| 83 | +bugs), you will need to use your best judgment to apply the guiding principles |
| 84 | +at the top of this document. The most appropriate warning for behavioral |
| 85 | +changes is usually `FutureWarning`. Some possibilities for how to effect a |
| 86 | +change: |
| 87 | + |
| 88 | +- If you are changing the default behavior of a function, consider adding a |
| 89 | + keyword argument to select between old and new behaviors. When it comes time, |
| 90 | + you can issue a `FutureWarning` if the keyword argument is not given |
| 91 | + (*e.g.* if it is `None`), saying that the new value will soon become the |
| 92 | + default. You will need to go through the normal deprecation period for |
| 93 | + removing this keyword argument after you have made the behavior change. This |
| 94 | + will take at least six months to go through both cycles. |
| 95 | + |
| 96 | +- If you need to change the return type of a function, consider adding a new |
| 97 | + function that returns the new type, and then follow the procedures for |
| 98 | + deprecating the old function. |
| 99 | + |
| 100 | +- If you need to accept a new input that you cannot distinguish from an existing |
| 101 | + possibility because of its type, consider letting it be passed by a different |
| 102 | + keyword argument, or add a second function that only accepts the new form. |
| 103 | + |
| 104 | + |
| 105 | + |
| 106 | +## Issuing deprecation warnings |
| 107 | + |
| 108 | +The proper way to raise a deprecation warning is to use the `@deprecate_function` decorator, and |
| 109 | +the `deprecate_arguments` and `issue_deprecation_msg` functions |
| 110 | +from `qiskit_ibm_runtime.utils.deprecation`. |
| 111 | +These will generate a standardized message and ensure an alternative path is specified. |
| 112 | + |
| 113 | +Usually, you should set `remedy: str` with the format `"Instead, use ..."` so that |
| 114 | +people know how to migrate. Read those functions' docstrings for additional arguments like |
| 115 | +`version: str`. |
| 116 | + |
| 117 | +If the functions in `qiskit_ibm_runtime.utils.deprecation` cannot handle your use case, consider improving |
| 118 | +them. Otherwise, you can directly call the `warn` function |
| 119 | +from the [warnings module in the Python standard library](https://docs.python.org/3/library/warnings.html), |
| 120 | +using the category `DeprecationWarning`. For example: |
| 121 | + |
| 122 | +```python |
| 123 | +import warnings |
| 124 | + |
| 125 | +def deprecated_function(): |
| 126 | + warnings.warn( |
| 127 | + "The function qiskit.deprecated_function() is deprecated since " |
| 128 | + "qiskit-ibm-runtime 0.14.0, and will be removed 3 months or more later. " |
| 129 | + "Instead, you should use qiskit.other_function().", |
| 130 | + category=DeprecationWarning, |
| 131 | + stacklevel=2, |
| 132 | + ) |
| 133 | + # ... the rest of the function ... |
| 134 | + |
| 135 | +``` |
| 136 | + |
| 137 | +Make sure you include the version of the package that introduced the deprecation |
| 138 | +warning (so maintainers can easily see when it is valid to remove it), and what |
| 139 | +the alternative path is. |
| 140 | + |
| 141 | +Take note of the `stacklevel` argument. This controls which function is |
| 142 | +accused of being deprecated. Setting `stacklevel=1` means the |
| 143 | +warning will blame the `warn` function itself, while `stacklevel=2` (the default) will |
| 144 | +correctly blame the containing function. It is unusual to set this to anything |
| 145 | +other than `2`, but can be useful if you use a helper function to issue the |
| 146 | +same warning in multiple places. |
| 147 | + |
| 148 | + |
| 149 | +## Testing deprecated functionality |
| 150 | + |
| 151 | +Whenever you add deprecation warnings, you will need to update tests involving |
| 152 | +the functionality. The test suite should fail otherwise, because of the new |
| 153 | +warnings. We must continue to test deprecated functionality throughout the |
| 154 | +deprecation period, to ensure that it still works. |
| 155 | + |
| 156 | +To update the tests, you need to wrap each call of deprecated behavior in its |
| 157 | +own assertion block. For subclasses of `unittest.TestCase` (which all Qiskit |
| 158 | +test cases are), this is done by: |
| 159 | + |
| 160 | + |
| 161 | +```python |
| 162 | +class MyTestSuite(QiskitTestCase): |
| 163 | + def test_deprecated_function(self): |
| 164 | + with self.assertWarns(DeprecationWarning): |
| 165 | + output = deprecated_function() |
| 166 | + # ... do some things with output ... |
| 167 | + self.assertEqual(output, expected) |
| 168 | +``` |
| 169 | + |
| 170 | +## Documenting deprecations and breaking changes |
| 171 | + |
| 172 | +It is important to warn the user when your breaking changes are coming. |
| 173 | + |
| 174 | +Make sure to update the docstring of the function, so that it shows up in |
| 175 | +API reference. |
| 176 | + |
| 177 | +You can add a [Sphinx deprecated directive](https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-deprecated): |
| 178 | + |
| 179 | + |
| 180 | +```python |
| 181 | +def deprecated_function(): |
| 182 | + """ |
| 183 | + Short description of the deprecated function. |
| 184 | +
|
| 185 | + .. deprecated:: 0.14.0 |
| 186 | + The function qiskit_ibm_runtime.deprecated_function() is deprecated since |
| 187 | + qiskit_ibm_runtime 0.14.0, and will be removed 3 months or more later. |
| 188 | + Instead, you should use qiskit_ibm_runtime.other_function(). |
| 189 | +
|
| 190 | + <rest of the docstring> |
| 191 | + """ |
| 192 | + # ... the rest of the function ... |
| 193 | +``` |
| 194 | + |
| 195 | + |
| 196 | +You should also document the deprecation in the changelog by using Reno. Explain the deprecation |
| 197 | +and how to migrate. |
| 198 | + |
| 199 | +In particular situations where a deprecation or change might be a major disruptor for users, a |
| 200 | +*migration guide* might be needed. Please write these guides in Qiskit's documentation at |
| 201 | +https://github.com/Qiskit/documentation/tree/main/docs/api/migration-guides. Once |
| 202 | +the migration guide is written and published, deprecation |
| 203 | +messages and documentation should link to it. |
0 commit comments