From ec7d1d4c2b39010658b385e4db31918d007e6e03 Mon Sep 17 00:00:00 2001
From: Mark Hulbert <39801222+m-hulbert@users.noreply.github.com>
Date: Tue, 12 Dec 2023 11:47:00 +0000
Subject: [PATCH] Migrate content to relevant sections and remove best practice
guide
---
content/auth/identified-clients.textile | 10 +-
content/channels/index.textile | 8 +
content/connect/index.textile | 51 ++--
content/connect/states.textile | 20 +-
content/presence-occupancy/presence.textile | 37 ++-
content/root/best-practice-guide.textile | 268 --------------------
data/yaml/page-furniture/sidebar.yaml | 2 -
7 files changed, 84 insertions(+), 312 deletions(-)
delete mode 100644 content/root/best-practice-guide.textile
diff --git a/content/auth/identified-clients.textile b/content/auth/identified-clients.textile
index 12be064bdc..365ac8d243 100644
--- a/content/auth/identified-clients.textile
+++ b/content/auth/identified-clients.textile
@@ -14,7 +14,7 @@ languages:
- swift
---
-When a client is authenticated and connected to Ably, it is considered to be an authenticated client. While an authenticated client has a means to authenticate with Ably, they do not necessarily have an identity.
+When a client is authenticated and connected to Ably, it is considered to be an authenticated client. While an authenticated client has a means to authenticate with Ably, they do not necessarily have an identity.
When a client is assigned a trusted identity, that is, a @clientId@, then they are considered to be an identified client. For all operations that client performs with the Ably service, their @clientId@ field will be automatically populated and can be trusted by other clients.
@@ -28,9 +28,11 @@ There are three different ways a client can be identified with using a @clientId
* A client is authenticating with a token issued for a specific @clientId@.
* A client claims a @clientId@ when authenticating with a token that is issued for a wildcard @clientId@.
+When a client sets their own ID there is the possibility that they can pretend to be someone else. To prevent this, it is recommended that you embed a @clientId@ in the token issued to your clients from your auth server. This ensures that the @clientId@ is set by your auth server, and eliminates the chance of a client pretending to be someone else.
+
h3(#basic). Basic auth
@@ -39,7 +41,7 @@ You can use "basic authentication":/auth/basic to allow a client to claim any @c
h3(#token). Token auth
-You can use "token authentication":/auth/token to set an explicit @clientId@ when creating or issuing a token. Clients using that token are restricted to operations for only that @clientId@, and all operations will implicitly contain that @clientId@.
+You can use "token authentication":/auth/token to set an explicit @clientId@ when creating or issuing a token. Clients using that token are restricted to operations for only that @clientId@, and all operations will implicitly contain that @clientId@.
For example, when publishing a message, the @clientId@ attribute of the message will be pre-populated with that @clientId@. Entering presence will also implicitly use that @clientId@.
@@ -184,7 +186,7 @@ The following example demonstrates how to issue an "Ably TokenRequest":/auth/tok
h3(#wildcard). Wildcard token auth
-You can use "token authentication":/auth/token to set a wildcard @clientId@ using a value of @*@ when creating a token. Clients are then able to assume any identity in their operations, such as when publishing a message or entering presence.
+You can use "token authentication":/auth/token to set a wildcard @clientId@ using a value of @*@ when creating a token. Clients are then able to assume any identity in their operations, such as when publishing a message or entering presence.
h2(#unidentified). Unidentified clients
diff --git a/content/channels/index.textile b/content/channels/index.textile
index 3328c84fbd..a2a54d30ab 100644
--- a/content/channels/index.textile
+++ b/content/channels/index.textile
@@ -67,6 +67,10 @@ h2(#create). Create or retrieve a channel
A @Channel@ object is a reference to a single channel and is uniquely identified by its unicode string name. A channel is created, or an existing channel is retrieved from the @Channels@ collection, using the "@get()@":/api/realtime-sdk/channels#get method. You can only connect to one channel in a single operation. Wildcards are not supported.
+Although Ably recommends that you use channels to distribute work more evenly across the cluster, there is an associated cost for a high number of channels. Don't use different channels just to indicate different types of data, or different events, if all messages are going to the same set of clients. Use a single channel and distinguish between them using a different message @name@.
+
+Channels are the unit of security and scalability. If you are sending data that must not be shared with certain clients, ensure it is on a channel that those clients don't have the "capabilities":/auth/capabilities to "attach":#attach to.
+
The following is an example of creating a channel:
```[realtime_javascript]
@@ -667,6 +671,8 @@ h2(#subscribe). Subscribe to a channel
Subscribe to a channel in order to receive messages being published to it, by registering a listener. Subscribing is an operation available to the realtime interface and uses the "@subscribe()@":/api/realtime-sdk/channels#subscribe method.
+Subscribing to events server-side using the Pub/Sub method can be disadvantageous as it can increase latency or duplicate events among multiple servers. "Message Queues":/general/queues are more a appropriate method to use in that instance, as multiple worker servers enable Ably to distribute the load of messages received from published. This means that each message is only processed once by any one of your worker servers.
+
A client can subscribe to all messages published to a channel by passing a listener function to the @subscribe()@ method. The listener is passed a "@Message@":/api/realtime-sdk/types#message object for each message received. Alternatively, a client can listen for a subset of messages based on the name of the published message.
@@ -1453,6 +1459,8 @@ Previously registered listeners can be removed individually or all together.
stateChangeListener.cancel();
```
+Be aware that when registering listeners for channel state changes, certain repeating states may add new listeners each time.
+
h2(#failure). Handle channel failures
Channel attach and detach operations are asynchronous. After initiating an attach request, the client will wait for a response from Ably that confirms that the channel is established on the service and then trigger a "state change":#states event.
diff --git a/content/connect/index.textile b/content/connect/index.textile
index 76b9cfcc3a..5fd19e7b43 100644
--- a/content/connect/index.textile
+++ b/content/connect/index.textile
@@ -19,7 +19,7 @@ redirect_from:
- /realtime/versions/v0.8/connection
---
-Clients establish and maintain a connection to the Ably service using the most efficient transport available, typically "WebSockets":https://ably.com/topic/websockets. Ably SDKs operate and multiplex all "channel":/channels traffic over that connection. Once connected, clients can monitor and manage their "connection state":/connect/states.
+Clients establish and maintain a connection to the Ably service using the most efficient transport available, typically "WebSockets":https://ably.com/topic/websockets. Ably SDKs operate and multiplex all "channel":/channels traffic over that connection. This maximizes throughput, minimizes bandwidth consumption, and reduces power usage. Once connected, clients can monitor and manage their "connection state":/connect/states.
h3(#present-no-subscribe). Be present without subscribing to presence events
@@ -421,7 +422,7 @@ Efficient use of capabilities can prevent clients from receiving presence events
One example of achieving this would be to use one channel for generic communications and another for the presence set. The following capabilities demonstrate this for clients and for servers:
-For clients:
+For clients:
```[json]
{
@@ -443,9 +444,9 @@ Alternatively, "channel mode flags":/channels/options#modes can be used to enabl
h2(#retrieve-members). Retrieve presence members
-The membership of the presence set can be retrieved by calling the "get()":/api/realtime-sdk/presence#get method on the @Presence@ object of a channel. This returns an array of all members currently present on the channel and is available using the REST and realtime interfaces of an Ably SDK.
+The membership of the presence set can be retrieved by calling the "@get()@":/api/realtime-sdk/presence#get method on the @Presence@ object of a channel. This returns an array of all members currently present on the channel and is available using the REST and realtime interfaces of an Ably SDK.
-An Ably client connected using the realtime interface of an SDK is responsible for keeping track of the presence set from the time that the channel is attached. An up to date presence set is pushed to the client following a channel attachment, and the presence set is updated on each subsequent presence event. This means that "get()":/api/realtime-sdk/presence#get returns the already known presence set retained in memory and does not trigger a new request to the Ably service.
+An Ably client connected using the realtime interface of an SDK is responsible for keeping track of the presence set from the time that the channel is attached. An up to date presence set is pushed to the client following a channel attachment, and the presence set is updated on each subsequent presence event. This means that "@get()@":/api/realtime-sdk/presence#get returns the already known presence set retained in memory and does not trigger a new request to the Ably service.
The REST interface of an Ably SDK queries "the REST API":/api/rest-api#presence directly. No presence state is cached in the SDK itself.
@@ -590,6 +591,28 @@ if page.hasNext() {
}
```
+h3(#synced). Synced presence set
+
+A common requirement of the presence set is to keep an updated list of members that are currently present on a channel in your user interface.
+
+Many developers try to build the initial list using the "@get()@":/api/realtime-sdk/presence#get method and then mutate that list whenever a new presence event arrives. Ably advises against this approach, because it's easy to quickly go wrong and end up with a list that's out of sync with the real presence set.
+
+One common error is to fail to take into account that a single @clientId@ may be present multiple times on the same channel via different "connections":/connect. As far as Ably is concerned, these are different members of the presence set as they have different @connectionId@s. For example, if a client with the ID “Sarah” is connected to a channel on both a desktop and a mobile device simultaneously, or via multiple tabs in a browser, “Sarah” will be present twice in the presence set with the same @clientID@. If "Sarah" leaves the channel on her mobile, your app sees the @leave@ event and incorrectly removes her entry from the list.
+
+Ably recommends that you instead just @get()@ the presence set afresh from the Ably SDK whenever you see a presence event and use it to rebuild the list of members in your user interface. The @get()@ operation is free and a local call to the SDK and provides you the presence set that the client has already synced with the server. The server keeps the presence set up to date and there is not cost to using this approach.
+
+The following is an example of calling the @get()@ method on presence messages:
+
+```[realtime_javascript]
+channel.presence.on((presenceMessage) => {
+ // Ignore the presence message, just rebuild your state from the get() method
+ // (including uniquifying by clientId if you only want one entry per clientId)
+ channel.presence.get((err, members) => {
+ console.log('There are ' + members.length + ' members on this channel');
+ });
+});
+```
+
h3(#batch). Batch presence
It is possible to retrieve the presence state of multiple channels in a single request. The presence state contains details about the clients in the presence set, such as their @clientId@, member data and "presence action":/api/realtime-sdk/types#presence-action.
@@ -628,7 +651,7 @@ ablyRest.request('GET', '/presence', content, null, {}, function(err, response)
});
```
-The following is an example curl request, querying the REST API directly:
+The following is an example curl request, querying the REST API directly:
```[sh]
curl -X GET https://rest.ably.io/presence?channel=channel1,channel2 \
@@ -802,4 +825,4 @@ bc[jsall]. const ably = new Ably.Realtime(
key: '{{API_KEY}}',
transportParams: { remainPresentFor: 1000 }
}
-);
\ No newline at end of file
+);
diff --git a/content/root/best-practice-guide.textile b/content/root/best-practice-guide.textile
deleted file mode 100644
index af5888ddda..0000000000
--- a/content/root/best-practice-guide.textile
+++ /dev/null
@@ -1,268 +0,0 @@
----
-title: Best Practice Guide
-meta_description: "This topic provides guidance on some best practices when using Ably."
-meta_keywords: "Best practice, FAQs, hints and tips"
-section: root
-index: 30
-jump_to:
- Contents:
- - Ably 101
- - For experienced developers
----
-
-This best practice guide is designed to help you make the most out of Ably's Realtime platform. These recommendations include some important considerations that may otherwise be missed when reading through our documentation.
-
-This guide consists of the following two sections:
-
-* "Ably 101":#ably-101 - Key concepts that you must understand before moving on to the best practice recommendations.
-* "For experienced developers":#for-experienced-developers - Ably's recommendations for experienced developers, to help avoid common issues.
-
-h2(#ably-101). Ably 101
-
-Ably Realtime is a cloud-based platform that enables you to integrate realtime functionality into your applications.
-
-h3(#auth). Authentication
-
-Ably supports two methods of authentication:
-
-1. "Basic authentication":/auth/basic
-2. "Token authentication":/auth/token
-
-When using basic authentication, you use the Ably API key to authenticate with Ably. In production systems you should always avoid using basic authentication on the client-side (such as in a browser), where the API key might be exposed. Instead, use "token authentication":/auth/token with either an @authUrl@ or @authCallback@.
-
-With token authentication, your auth server uses your Ably API key to authenticate with Ably and then provides a @TokenRequest@ object to your client, which your client then uses to authenticate with Ably. These short-lived tokens ensure that the Ably API key is never exposed to your client-side applications. However, the auth server can use the API key directly to authenticate with Ably using basic authentication, as on the secure server the API key will not be accessible to anyone other than authenticated personnel, such as systems administrators.
-
-**Important:** Do not use basic authentication on the client-side, as it exposes your Ably API key. API keys don't have an expiry, so once compromised, they could be used indefinitely by an attacker. Tokens have an expiry, and so there is only a small period of time during which the compromised token can be used.
-
-**Important:** Never use token authentication to authenticate your server with Ably, as this results in unnecessary overhead - the server will periodically need to reauthenticate with Ably. Instead, use basic authentication to authenticate your server with Ably.
-
-If you prefer to use a single auth strategy across all third-party services, you can use "JWTs":/auth/token#ably-jwt-process to authenticate with Ably.
-
-h3(#using-api-key-client-side). Using the API key on the client side
-
-"Basic authentication":/auth/basic is the simplest method to authenticate your clients on the Ably platform, but this exposes your API key in the source code. Therefore, Ably recommends that you create an auth server and use "token authentication":/auth/token to authenticate your clients. Using this approach, only your auth server has access to your API key, protecting it from being misused. "Read more":/auth#selecting-auth about selecting an authentication mechanism.
-
-h3(#ensuring-tokens-are-auto-renewable). Renewing tokens
-
-Tokens are short-lived, that is, they expire after a given time. You must ensure that the client can request a new token when it needs one. The Ably Client Library SDKs provide two different mechanisms to obtain tokens automatically: "@authUrl@":/realtime/authentication#auth-url or "@authCallback@":/realtime/authentication#auth-callback. You specify which one to use in the "ClientOptions":/realtime/authentication#token-authentication object.
-
-h3(#rest-or-realtime). Realtime vs REST
-
-Ably Client Library SDKs may implement two interfaces: realtime and REST. The "Realtime":/realtime interface maintains a persistent connection to Ably and can be considered stateful. The "REST":/rest interface issues HTTP requests to the Ably platform and is stateless. The REST interface is therefore lighter weight because it does not need to maintain state.
-
-Depending on your use case, you may only need to use the REST interface on your server. For example, the server may only need to authenticate with Ably, and then act as an auth server for clients. You might then use the realtime interface on your client, so it can both subscribe to and publish messages.
-
-The PHP and Python SDKs only have a REST interface. While the Ruby SDK implements both realtime and REST interfaces, there is also a Ruby SDK available that implements only the REST interface. The full list of available SDKs, and their supported interfaces, can be found on the "SDK download":https://ably.com/download page.
-
-An important difference between the REST and realtime interface is that with REST you can only publish realtime messages and not subscribe to them. With the realtime interface you can both publish and subscribe. If you have a use case where you only need to authenticate, or publish messages, then the REST interface is recommended.
-
-The main factors are summarized here:
-
-* If you want to maintain a persistent connection with Ably's Realtime platform in order to subscribe to messages sent on a channel, or be notified whenever the presence set changes, you need to use the "realtime library":/realtime.
-* If you just want to publish data to the platform and don't need to subscribe or manage channel attachment, you can the "REST library":/rest.
-* If you want to publish at very high rates or with the lowest possible realtime latencies, "Ably's realtime library":/realtime would be best as it can publish at higher rates and lower latencies than is possible with the REST library due to message pipelining.
-* If you want to primarily issue tokens for clients or publish messages to channels, you can use the "REST library":/rest.
-* If you want to get one-time info about the presence set of a channel, you can use the "REST library":/rest.
-* If you want to set up realtime communication with IoT devices on platforms for which there isn't an Ably Client Library SDK available, you can use the "MQTT protocol adapter":/protocols/mqtt.
-* If you wish to migrate from PubNub or Pusher to Ably, you can use the relevant "protocol adapter":https://ably.com/protocols to "switch to Ably":https://faqs.ably.com/can-you-help-ease-the-pain-of-migration-from-another-data-stream-network, and then gradually move over to using the Ably Client Library SDKs.
-
-h3(#encryption). Encryption
-
-All Ably Client Library SDKs "use TLS by default":https://faqs.ably.com/are-messages-sent-to-and-received-from-ably-securely-using-tls. This ensures that data is transmitted to and from Ably securely. However, messages are not encrypted _within_ the Ably system. Using the "encryption feature":/channels/options/encryption of our SDKs, you can ensure that message payloads are opaque: they can never be decrypted by Ably but only by other clients that share your secret key.
-
-h3(#pubsub). Publish and Subscribe (Pub/Sub)
-
-You can publish data to named "channels":/realtime/channels within Ably's platform and the platform will make sure that all clients subscribed to those channels will receive the data in realtime.
-
-h3(#notifications). Notifications
-
-Ably offers the "Push Notifications":/general/push feature which enables you to send notifications to your clients even when they are not currently using your application and are therefore not connected to Ably. This can be implemented on both mobile devices and browsers.
-
-h3(#presence). Presence
-
-With Ably's "Presence":/presence-occupancy/presence feature, your clients can attach to a presence channel and announce to other channel users that they are present on a channel. This is very useful, for example, in chat apps to indicate a member is online, or with IoT devices to indicate a device is online and connected. Clients can subscribe to realtime presence changes on channels, such as members entering or leaving, and respond to these events accordingly.
-
-h2(#for-experienced-developers). For experienced developers
-
-In order to make the best use of Ably, you need to be aware of the following information:
-
-h3(#attach-vs-subscribe). Attaching versus subscribing to a channel
-
-It is important to understand the difference between attaching and subscribing to a channel, and that messages are sent to clients as soon as they attach to a channel.
-
-The "@attach@":/api/realtime-sdk/channels#attach method attaches a client to the channel. Published messages are immediately sent to clients if they have subscribe capabilities to that channel, regardless of whether or not the client has subscribed to the channel.
-
-The "@subscribe@":/api/realtime-sdk/channels#subscribe method registers a subscribe listener for messages received on the channel. @subscribe@ is a client-side operation, meaning Ably is unaware of whether or not a client is subscribed to the channel. @attach@ is called implicitly by the @subscribe@ method.
-
-h3(#detach-vs-unsubscribe). Detaching versus unsubscribing from a channel
-
-
-
-h3(#using-subscribe-filters). Use separate channels to filter messages for different sets of subscribers
-
-By default, all data published to a channel is pushed by Ably to every subscriber attached to that channel. Only on the client side can subscribers filter messages by their message name and so 'avoid' receiving all published messages. But, if you have messages that don't need to be sent to some clients at all, you're often better off using a separate channel for them rather than implementing a filter. Because this way, you won't be sending unnecessary messages that count towards your monthly message quota. This may be economical, but it isn't always the best solution as in some cases you may wish to trigger different listeners for different events and so setting up a single channel and filtering the messages client side would be more sensible. An example is shown below:
-
-```[javascript]
-const sportsChannel = realtime.channels.get("sport");
-//publish
-sportsChannel.publish("update", { "team": "Man United" });
-sportsChannel.publish("add", { "team": "Chelsea" });
-
-//subscribe filters
-sportsChannel.subscribe("update", () => {
- //do one thing
-});
-sportsChannel.subscribe("add", () => {
- //do another thing
-});
-```
-
-h3(#handling-connection-channel-state-change-events). Handling connection and channel state-change events
-
-Ably's Realtime SDKs automatically handle disruptions such as network disconnections, so you don't need to manually handle reconnection. The library emits:
-
-* "Connection":/connect/states#connection-states state change events such as @initialized@, @connecting@, etc.You can set up listeners for these state change events to handle connections failures like those cause by failed authentication. An example of handling connection state-change event is shown below:
-
-```[javascript]
-realtime.connection.on((stateChange) => {
- console.log('New connection state is ' + stateChange.current);
-});
-```
-
-* "Channel":/realtime/channels#channel-states state change events for various channel states such as @initialized@, @attaching@, etc. Similar to connection state changes, you can set up listeners for channel state changes in case you'd like to be informed about what's happening. For example, if you want to know whenever the library has lost message continuity on a channel due to being disconnected for more than two minutes. An example of handling channel state-change events is shown below:
-
-```[javascript]
-const myListener = (stateChange) => {
- console.log('channel state is ' + stateChange.current);
- console.log('previous state was ' + stateChange.previous);
-});
-channel.on(myListener);
-```
-
-However, while registering listeners for both connection and channel state change events, bear in mind that certain repeating states might add new listeners each time. For instance, registering a listener for @on(connected)@ adds a new listener every time the client becomes connected, even if this is a reconnection after being offline for a while, which is often an unintentional result.
-
-h3(#unsubscribe-vs-off). Unsubscribe and close versus off
-
-It is important to understand the difference between unsubscribing from a channel and closing a connection, compared to calling the @off()@ method for a channel or connection.
-
-The "@unsubscribe()@":/api/realtime-sdk/channels#unsubscribe method removes message listeners for a channel.
-
-The "@close()@":/api/realtime-sdk/connection#close method closes a realtime connection.
-
-The @off()@ method for a "channel":/api/realtime-sdk/channels#off or "connection":/api/realtime-sdk/connection#off removes @ChannelEvent@ or @ConnectionEvent@ listeners that are listening for state changes on a channel or for a connection.
-
-h3(#subscribing-to-events-server-side). Subscribing to events on the server-side
-
-You should be aware that subscribing to events on the server side, using the standard Pub/Sub pattern can be disadvantageous because it can increase latency or duplicate events among multiple servers, as explained in this "blog article":https://ably.com/blog/message-queues-the-right-way. With Message Queues, you can have multiple worker servers and allow Ably to distribute the load of the messages received from publishers, so that each message is only processed once by any one of your worker servers.
-
-h3(#choosing-between-webhooks-functions-queues). Choosing between webhooks, message queues, and Firehose
-
-"Webhooks":/general/webhooks work well for low or medium message rates of approximately 20/s, but "Message Queues":/general/queues scale better to high message rates of around 200/s. For even higher rates, Ably recommends using "Firehose":/general/firehose.
-
-h3(#multiplexing-channels). Multiplexing channels
-
-Always strive to instantiate a single Ably Client Library SDK instance per device, web page or server. Don't forget that the SDK can "multiplex":https://faqs.ably.com/do-you-support-multiplexing-and-channel-groups many channels over a single connection. This maximizes throughput, minimizes bandwidth consumption, and reduces power usage.
-
-h3(#principle-of-least-privilege). Limiting the effect of a security compromise using the principle of least privilege
-
-Using the available "authentication methods":/auth in Ably, you can choose the permissions or capabilities to be given to a client. You can do this by restricting the permissions on the API key as well as restricting the permissions that your auth server grants to this API key whilst sending a token request. You can restrict the channels that a client can access (using wildcards), as well as the operations the client can perform within those channels, using "capabilities":/auth/capabilities.
-
-Always make sure to limit the capabilities given to a client to only what's required by the client to do their job. This prevents any unexpected behaviour by a client and increases the security of your application. This is called the principle of least privilege.
-
-h3(#efficient-use-of-channels). Efficient use of channels
-
-Although Ably recommends that you use channels to distribute work more evenly across the cluster, there is an associated cost for a high number of channels. Here are some considerations that might help you plan your channel usage:
-
-* Don't use different channels just to indicate different types of data or different events, if they're all going to the same set of clients. Use one channel, and distinguish between the different events using the message name.
-* Channels are the unit of security and scalability. If you are sending some data that must not be shared with certain clients, make sure it's on a channel that those clients don't have the capabilities to attach to.
-* Be aware that each channel can support a maximum of 100 messages per second.
-
-h3(#embedding-client-id-in-token). Embedding client ID in the token to ensure trust
-
-A client ID serves as an identification for your clients when they are present on a channel. Your clients can set their own ID using the @clientId@ attribute, but in that case, there's a possibility that some of your clients can pretend to be someone else. To prevent this, you can embed a @clientId@ in the token issued to your clients from your auth server. Doing so ensures that the @clientId@ is set by your auth server, thus eliminating the chance of the end clients emulating as others.
-
-h3(#presence-volume). Understanding the number of presence messages that can be produced
-
-It is important when using the "presence":/presence-occupancy/presence feature to understand the number of messages it can produce. If you have subscribers and presence members present on a channel then the number of messages used can increase rapidly. As an example, consider 200 clients subscribed to presence events on a channel and all of them join and leave the presence set within a few minutes. This would result in the following messages:
-
-* 200 presence messages published for the enter event.
-* 200 x 200 (40,000) messages subscribed to for the enter events.
-* 200 presence messages published for the leave event.
-* 200 x 200 (40,000) presence messages subscribed to for the leave event.
-
-This highlights the potential for 80,400 messages to be sent in a very short space of time on a single channel. As additional clients subscribe and enter the presence set, the number of messages quickly multiplies.
-
-In some instances this is the behavior required of your application. In other scenarios you may prefer to only have a server subscribed to presence events and the clients are only present on the channel.
-
-h3(#being-present-without-subscribing-to-presence). Being present without subscribing to presence events
-
-It is possible to have clients be present on a channel without them receiving "presence change events":/realtime/presence#presence-states. Efficient use of "capabilities":/auth/capabilities can prevent all clients from receiving presence notifications while still allowing them to enter the presence set. This is a common scenario when only a server is required to monitor the presence set and saves clients from subscribing to a potentially high volume of unnecessary messages.
-
-An example of achieving this would be to use one channel for generic communications and another for the presence set. The following capabilities demonstrate this for clients and for servers:
-
-For clients:
-
-```[json]
-{
- "chatPresence": ["presence"],
- "chat": ["publish", "history", "subscribe"],
-}
-```
-
-For servers:
-
-```[json]
-{
- "chatPresence": ["presence", "subscribe"],
- "chat": ["publish", "history", "subscribe"],
-}
-```
-
-
-h3(#use-existing-presence-set). Using the synced presence set provided by the realtime library
-
-A common need is to keep an updated list of currently-present members in your user interface.
-
-To achieve this, many developers try to build the initial list using "@Presence.get()@":/api/realtime-sdk/presence#get and then mutate that list whenever a new presence event arrives, from that event. However, we recommend against doing this, because it's easy to get wrong and end up with a list that's out of sync with the real presence set.
-
-One common error is to fail to take into account that a single @clientId@ may be present multiple times on the same channel via different client connections; as far as Ably is concerned, these are different members of the presence set for the channel as they have different @connectionId@s. For example, if a client with ID “Sarah” is connected to a chat channel on both a desktop and a mobile device simultaneously, or maybe via multiple tabs in a browser, “Sarah” will be present twice in the presence member set with the same @clientID@. So if she leaves the channel on her mobile, and your app sees the @leave@ event and removes her entry from that list, you will get an incorrect list.
-
-Instead, our recommended best practice is to just get the presence set afresh from the client library whenever you see a presence event, and rebuild the list in your user interface from that.
-
-With a realtime SDK, the @get@ operation is free and local, it just gives you the presence set that the client has already synced with the server (and which is kept updated by the server), so there is no cost to this.
-
-For example:
-
-```[javascript]
-channel.presence.on((presenceMessage) => {
- // Ignore the presence message, just rebuild your state from the get() method
- // (including uniquifying by clientId if you only want one entry per clientId)
- channel.presence.get((err, members) => {
- console.log('There are ' + members.length + ' members on this channel');
- });
-});
-```
-
-h3(#testing-apps). App testing
-
-Ably apps are entirely isolated from one another, so each app can be treated as its own environment. Ably recommends using separate apps within your account for testing purposes and to name apps logically to easily differentiate between development, test and production environments.
-
-The Ably sandbox environment is strictly for use by Client Library SDK developers and should not be used for testing.
diff --git a/data/yaml/page-furniture/sidebar.yaml b/data/yaml/page-furniture/sidebar.yaml
index b33be84445..34d5f43d9f 100644
--- a/data/yaml/page-furniture/sidebar.yaml
+++ b/data/yaml/page-furniture/sidebar.yaml
@@ -171,8 +171,6 @@ items:
link: /docs/api
- label: 'Glossary'
link: /docs/glossary
- - label: 'Best Practice Guide'
- link: /docs/best-practice-guide
- label: 'Limits'
link: /docs/general/limits
- label: 'Platform Customization'