Once we've thought about the dependencies of the render array, the second most important thing to consider is what it differs by. In other words, is there any reason why this render array should be shown one way sometimes but another way some other time?
Let's take a simple example of a render array that prints out the name of the current user. Nothing could be less complicated. Ignoring the cache tags for now, we immediately realize that we cannot show the same username to all users, right? So, the user Danny should see "Hi Danny" while user John should see "Hi John". We are talking about the same render array but one that differs by context. In other words, a variation of this render array needs to get cached separately for each encountered context. This is where we use the aforementioned cache contexts.
Similar to cache tags, cache contexts are simple strings, and a render array can be defined with more than just one. For example, the user context will cache a variation of a given render array for each user.
Moreover, they are hierarchical in nature in the sense that some contexts can include others. For example, let's continue with our previous example. Let's assume that users with the editor role should see the greeting message but the ones with the contributor role should see a different, more complicated one. In this case, the cache context would be on the roles the user has. But since it already depends on the actual user due to the need to show its username, it doesn't make sense to even bother with the roles context because the former encompasses the latter. Moreover, Drupal is smart enough to remove the superfluous one when combining the cache contexts from all the render arrays that make up a page. But if our render array differs, for example, only on the user roles and not necessarily the user itself, we should use the specific context—user.roles. As you may notice, the hierarchical nature is reflected in the dot (.) separation of the contexts.
There are a number of cache contexts already defined by Drupal core. Although you probably won't have to, at least in the beginning, you can define other contexts too. I recommend you check out the documentation page (https://www.drupal.org/docs/8/api/cache-api/cache-contexts) for the available cache contexts that come out of the box.