# Upgrade Fearlessly with the MongoDB Stable API

Do you hesitate to upgrade MongoDB, for fear the new database will be incompatible with your existing code?

Once you've written and deployed your MongoDB application, you want to be able to upgrade your MongoDB database at will, without worrying that a behavior change will break your application. In the past, we've tried our best to ensure each database release is backward-compatible, while also adding new features. But sometimes we've had to break compatibility, because there was no other way to fix an issue or improve behavior. Besides, we didn't have a single definition of backward compatibility.

Solving this problem is more important now: We're releasing new versions four times a year instead of one, and we plan to go faster in the future. We want to help you upgrade frequently and take advantage of new features, but first you must feel confident you can upgrade safely. Ideally, you could immediately upgrade all your applications to the latest MongoDB whenever we release.

The MongoDB Stable API is how we will make this possible. The Stable API encompasses the subset of MongoDB commands that applications commonly use to read and write data, create collections and indexes, and so on. We commit to keeping these commands backward-compatible in new MongoDB versions. We can add new features (such as new command parameters, new aggregation operators, new commands, etc.) to the Stable API, but only in backward-compatible ways.

We follow this principle:

> For any API version V, if an application declares API version V and uses only behaviors in V, and it is deployed along with a specific version of an official driver, then it will experience no semantically significant behavior changes resulting from database upgrades so long as the new database supports V.

(What's a semantically **insignificant** behavior change? Examples include the text of some error message, the order of a query result if you **don't** explicitly sort it, or the performance of a particular query. Behaviors like these, which are not documented and don't affect correctness, may change from version to version.)

To use the Stable API, upgrade to the latest driver and create your application's MongoClient like this:

```js
client = MongoClient(
  "mongodb://host/",
  api={"version": "1", "strict": True})
  ```

For now, "1" is the only API version. Passing "strict": True means the database will reject all commands that aren't in the Stable API. For example, if you call replSetGetStatus, which isn't in the Stable API, you'll receive an error:

```js
{
  "ok" : 0,
  "errmsg" : "Provided apiStrict:true, but replSetGetStatus is not in API Version 1",
  "code" : 323,
  "codeName" : "APIStrictError"
}
```

Run your application's test suite with the new MongoClient options, see what commands and features you're using that are outside the Stable API, and migrate to versioned alternatives. For example, "mapreduce" is not in the Stable API but "aggregate" is. Once your application uses only the Stable API, you can redeploy it with the new MongoClient options, and be confident that future database upgrades won't affect your application.

The mongosh shell now supports the Stable API too:

```bash
mongosh --apiVersion 1 --apiStrict
```

You may need to use unversioned features in some part of your application, perhaps temporarily while you are migrating to the Stable API, perhaps permanently. The **escape hatch** is to create a non-strict MongoClient and use it just for using unversioned features:

```PYTHON
# Non-strict client.
client = MongoClient(
  "mongodb://host/",
  api={"version": "1", "strict": False})

client.admin.command({"replSetGetStatus": 1})
```

The "strict" option is false by default, I'm just being explicit here. Use this non-strict client for the few unversioned commands your application needs. Be aware that we occasionally make backwards-incompatible changes in these commands.

The only API version that exists today is "1", but in the future we'll release new API versions. This is exciting for us: MongoDB has a few warts that we had to keep for compatibility's sake, but the Stable API gives us a safe way to remove them. Consider the following:

```PYTHON
client = MongoClient("mongodb://host")
client.test.collection.insertOne({"a": 1]})

# Strangely, this matches the document above.
result = client.test.collection.findOne(
  {"a.b": {"$ne": null}})
  ```

It's clearly wrong that `{"a": [1]}` matches the query `{"a.b": {"$ne": null}}`, but we can't fix this behavior, for fear that users' applications rely on it. The Stable API gives us a way to safely fix this. We can provide cleaner query semantics in Version 2:

```PYTHON
# Explicitly opt in to new behavior.
client = MongoClient(
  "mongodb://host/",
  api={"version": "2", "strict": True})

client.test.collection.insertOne({"a": [1]})

# New behavior: doesn't match document above.
result = client.test.collection.findOne(
  {"a.b": {"$ne": null}})
  ```
  
Future versions of MongoDB will support **both** Version 1 and 2, and we'll maintain Version 1 for many years. Applications requesting the old or new versions can run concurrently against the same database. The default behavior will be Version 1 (for compatibility with old applications that don't request a specific version), but new applications can be written for Version 2 and get the new, obviously more sensible behavior.

Over time we'll deprecate some Version 1 features. That's a signal that when we introduce Version 2, those features won't be included. (Future MongoDB releases will support both Version 1 with deprecated features, and Version 2 without them.) When the time comes for you to migrate an existing application from Version 1 to 2, your first step will be to find all the deprecated features it uses:

```PYTHON
# Catch uses of features deprecated in Version 1.
client = MongoClient(
  "mongodb://host/",
  api={"version": "1",
       "strict": True,
       "deprecationErrors": True})
```       

The database will return an APIDeprecationError whenever your code tries to use a deprecated feature. Once you've run your tests and fixed all the errors, you'll be ready to test your application with Version 2.

Version 2 might be a long way off, though. Until then, we're continuing to add features and make improvements in Version 1. We'll introduce new commands, new options, new aggregation operators, and so on. Each change to Version 1 will be an **extension** of the existing API, and it will never affect existing application code. With quarterly releases, we can improve MongoDB faster than ever before. Once you've upgraded to 5.0 and migrated your app to the Stable API, you can always use the latest release fearlessly.

You can try out the Stable API with the MongoDB 5.0 Release Candidate, which is available now from our [Download Center. 

## Appendix

Here's a list of commands included in API Version 1 in MongoDB 5.0. You can call these commands with version "1" and strict: true. (But of course, you can also call them without configuring your MongoClient's API version at all, just like before.) We won't make backwards-incompatible changes to any of these commands. In future releases, we may add features to these commands, and we may add new commands to Version 1.

* abortTransaction
* aggregate
* authenticate
* collMod
* commitTransaction
* create
* createIndexes
* delete
* drop
* dropDatabase
* dropIndexes
* endSessions
* explain (we won't make incompatible changes to this command's input parameters, although its output format may change arbitrarily)
* find
* findAndModify
* getMore
* hello
* insert
* killCursors
* listCollections
* listDatabases
* listIndexes
* ping
* refreshSessions
* saslContinue
* saslStart
* update

## Safe Harbor

The development, release, and timing of any features or functionality described for our products remains at our sole discretion. This information is merely intended to outline our general product direction and it should not be relied on in making a purchasing decision nor is this a commitment, promise or legal obligation to deliver any material, code, or functionality.