We want to move fast and bring out exciting new ideas quickly. We also appreciate users’ need for a solid, stable foundation to build on. This document explains what kind of stability the Drake project aims to provide, and what you can do to minimize the impact of the inevitable changes to Drake on your own work.
Rolling releases
Drake ships a new stable version approximately once per month. The versions are numbered “1.x.y”, where “x” increases by one for each stable version, and “y” reflects the patch version (typically zero). Drake does not follow SemVer. Instead, we use a rolling deprecation window. In each release, new deprecations might be announced, and then those changes will be finalized 3+ minor versions later, without changing the major version number.
Deprecation announcements appear in the
release notes for each stable release.
For C++ and Python API changes, we will also use the language mechanism to
highlight the change when possible ([[deprecated]]
for C++; warnings.warn
for Python). However, those mechanisms will not always trigger in every case,
and some deprecations will reach beyond what those tools can denote.
Therefore, we still recommend that you monitor the release notes as the final
arbiter of deprecation announcements.
Due to our 3-month announcement window, we recommend upgrading your pinned version of Drake at least that frequently. If you wish to upgrade less frequently, you still might benefit from rolling forward a few minor versions at a time, in order to obtain the built-in C++ and Python deprecation suggestions to guide you.
In general, we do not plan to backport fixes into prior stable releases.
Stable API
We define a large portion of Drake as our “Stable API” that you can rely on. For the Stable API, we aim to give at least 3 months of notice ahead of any disruption (via deprecation announcements in Drake’s release notes, and with compile-time or run-time warnings when feasible). Any element of Drake that is not explicitly documented to be part of the Stable API is deemed “unstable” and is subject to change or removal without prior notice.
The sub-headings below explain the Stable API for various facets of Drake.
Specific code or tools beyond what is listed here can also opt-in to be part of the Stable API in its local documentation. Those opt-ins are not enumerated here.
On very rare occasions it is impractical, too expensive, or logically unsound to meet the full three month deprecation window. In these cases the change will be announced in the “Breaking changes” section of the release notes.
C++
The C++ Stable API covers all C++ library code within namespace drake
whose
header files are distributed in a
Drake pre-compiled binary image.
- Excluding the
drake/examples/...
directory tree. - Excluding code within
namespace internal
. - Excluding code documented as “internal use only”.
- Excluding code documented with an “experimental” warning.
In particular, note that any directory named “dev” is necessarily excluded, because its header files are not installed.
Python
The Python Stable API covers all Python library code under import drake
or
import pydrake
that is distributed in a
Drake PyPI wheel:
- Excluding the
pydrake.examples...
module tree. - Excluding names that begin with a leading underscore, which denotes “internal use only” by convention of PEP-8.
- Excluding code documented as “internal use only”.
- Excluding code documented with an “experimental” warning.
- Excluding the “all” modules such as
pydrake.all
,pydrake.systems.all
, etc. These are intended to be shortcuts for temporary hacking only, and so are not Stable.- However, the existence of the
pydrake.all
module is part of the Stable API (we will not remove it without notice), but the specific list of items contained within it is not Stable.
- However, the existence of the
Build system
The Bazel Stable API covers all Drake Bazel targets (e.g., @drake//common
)
with public visibility that refer to C++ or Python library code that is itself
part of the “Stable API”:
- This includes
depending on
those labels (e.g.,
deps = ["@drake//common"]
). - This excludes
loading
Drake’s bzl macros (e.g.,
load("@drake//foo:bar.bzl", "bar_macro")
.
For Drake’s dependencies:
- The
add_default_...
macros defined in@drake//tools/workspace:default.bzl
are all part of the Stable API.- For any Bazel external loaded by these functions (e.g.,
"@eigen"
), we will deprecate it prior to removing our definition of the dependency.- Excluding any items documented as “internal use only”.
- Excluding any items documented with an “experimental” warning.
- For any Bazel external loaded by these functions (e.g.,
We may upgrade any of our dependencies to a newer version without prior notice. If you require an older version, you will need to rebuild Drake from source and pin your own WORKSPACE to refer to the older version of the dependency.
We may add new dependencies without prior notice. All of our dependencies will
either be installed via the host system via our install_prereqs
scripts,
and/or downloaded at build-time via our add_default_...
macros, and/or
specified via packaging metadata in the case of apt
or pip
.
LCM messages
Our Stable API covers all of Drake’s LCM message definitions:
- Excluding messages with “internal” as part of the message name.
- Excluding messages with “experimental” as part of the message name.
- Excluding messages documented as “internal use only”.
- Excluding messages documented as “experimental”.
Regarding changes to message definitions:
- LCM messages provide no mechanism for schema evolution or versioning.
- If we need to change a message definition (e.g.,
lcmt_schunk_wsg_command
), we will fork it into a newly-namedlcmt_schunk_wsg_command2
with the change, and deprecate the originallcmt_schunk_wsg_command
. - Drake code that uses that message type (e.g.,
SchunkWsgCommandSender
) will immediately switch to use the revised message, without deprecation. - This means that independent, direct use of the LCM message by users will have a smooth transition like any other library code, but any dependency on Drake’s sending or receiving a particular schema is not part of our “Stable API” promise.
Behavioral changes
Even if Drake does not change its API, sometimes its implementation may evolve in ways that affect your use. In the limit, any implementation change can be a breaking change.
Our goal is to keep our implementation within its documented API promises (C++, Python). Any significant changes to the API documentation should be announced via deprecation with 3 months of advance notice.
Sometimes our software will choose a default value when nothing more specific is provided. Those defaults may change as we find better heuristics, without prior notice. Similarly, our numerical algorithms may change in ways that alter the calculated results. There is no guarantee that Drake’s output is bitwise-identical from one version to the next.
Exemptions
Any new API will have these requirements waived for the first version where that API appears. (In other words, the second release of the API is the one where the Stable guarantees come into effect.) However, we will make a best effort to adhere to these guidelines as we resolve any issues with the new API, even in the first release.
Drake’s nightly builds are exempt from any stability guarantees.
Within the Stable API, we also expect you to meet certain requirements in order to obtain the stability guarantee:
- Include what you use.
- Drake’s
#include
graph will change over time; always#include
the header that matches each Drake class that you use. - Relatedly, do not include headers that you don’t use. Our C++ or Python deprecation warnings typically only trigger on call into the API, not mere inclusion or importing.
- Drake’s
- For Bazel users,
deps
what you use.- Drake’s
deps
graph will change over time; always list out your Drake library dependencies (to match what you#include
orimport
); do not rely on transitive dependencies to bring them in.
- Drake’s
- In C++, do not forward-declare anything from Drake.
- In C++, do not depend on the exact signature of Drake functions, as we may
add new, defaulted arguments without prior notice (i.e., do not take the
address of any Drake function, or assign it to a
std::function
). - In C++, do not depend on the exact order of
struct
fields. While the relative order of struct fields is guaranteed, new member fields might be inserted between existing fields. Consequently, when using aggregate initialization always use designated initializers (i.e., field names) when referring to struct fields.
Model files
Note in particular that any data files provided by Drake (e.g., SDFormat or URDF models) are not incorporated into the “Stable API”, even though some of those files are distributed in our stable releases for demonstration purposes. We may alter the models (e.g., changing the collision geometry or joint limits) or remove a model entirely, without prior notice.
ABI
Drake does not offer any stable ABI (Application Binary Interface). We expect any C++ code linked against Drake within the same program to use the identical source revision and build flags of Drake as all other code within that program.
Refer to Installation and Quickstart for the current details.
OS Support
Drake intends to support the two most recent versions of Ubuntu LTS and macOS on an ongoing basis. That generally means that your OS must be no more than ~2-4 years old for Ubuntu, or ~2 years old for macOS.
On Ubuntu, Drake only intends to support Ubuntu’s default version of Python (at
/usr/bin/python3
), except when installing from pip in which case any newer
version is also intended to be supported. This is consistent with
NEP-29.
On macOS, Drake only intends to support the one newest version of Python available via Homebrew. See #18791 for a discussion of possible improvements.
Refer to Installation and Quickstart for the current details, and End of support releases for historical details.