diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # hoauth2 Changelog
 
+## 2.15.1 (2026-04-17)
+- Dependency changes
+  - Relax the `containers` upper bound to `< 0.9` so `containers-0.8` is supported.
+  - No API or behavior changes.
+
 ## 2.15.0 (2025-10-05)
 - Breaking changes
   - Type and class renames
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-Haskell binding for
+Haskell bindings for
 
 -   [The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)
     -   If the Identity Provider also implements [OIDC
         spec](https://openid.net/specs/openid-connect-core-1_0.html), ID
-        Token will also be present in token response (see
-        `OAuth2Token`).
+        Token will also be present in the token response (see
+        `TokenResponse`).
 -   [JWT Profile for OAuth2 Client Authentication and Authorization
     Grants](https://www.rfc-editor.org/rfc/rfc7523.html)
 -   [The OAuth 2.0 Authorization Framework: Bearer Token
diff --git a/hoauth2.cabal b/hoauth2.cabal
--- a/hoauth2.cabal
+++ b/hoauth2.cabal
@@ -2,10 +2,10 @@
 name:               hoauth2
 
 -- http://wiki.haskell.org/Package_versioning_policy
-version:            2.15.0
+version:            2.15.1
 synopsis:           Haskell OAuth2 authentication client
 description:
-  This is Haskell binding of OAuth2 Authorization framework and Bearer Token Usage framework.
+  This package provides Haskell bindings for the OAuth2 Authorization Framework and Bearer Token Usage.
 
 homepage:           https://github.com/freizl/hoauth2
 license:            MIT
@@ -25,7 +25,7 @@
 
 source-repository head
   type:     git
-  location: git://github.com/freizl/hoauth2.git
+  location: https://github.com/freizl/hoauth2.git
 
 library
   hs-source-dirs:     src
@@ -75,7 +75,7 @@
     , binary                >=0.8    && <0.11
     , binary-instances      >=1.0    && <1.1
     , bytestring            >=0.9    && <0.13
-    , containers            >=0.6    && <0.8
+    , containers            >=0.6    && <0.9
     , crypton               >=0.32   && <1.1
     , data-default          ^>=0.8
     , exceptions            >=0.8.3  && <0.11
diff --git a/src/Network/OAuth2/AuthorizationRequest.hs b/src/Network/OAuth2/AuthorizationRequest.hs
--- a/src/Network/OAuth2/AuthorizationRequest.hs
+++ b/src/Network/OAuth2/AuthorizationRequest.hs
@@ -20,11 +20,11 @@
 
 -- | Authorization Code Grant Error Responses https://tools.ietf.org/html/rfc6749#section-4.1.2.1
 --
--- I found hard time to figure a way to test the authorization error flow
--- When anything wrong in @/authorize@ request, it will stuck at the Provider page
+-- I found it hard to figure out a way to test the authorization error flow.
+-- When anything goes wrong in an @/authorize@ request, it gets stuck at the provider page,
 -- hence no way for this library to parse error response.
 -- In other words, @/authorize@ ends up with 4xx or 5xx.
--- Revisit this whenever find a case OAuth2 provider redirects back to Relying party with errors.
+-- Revisit this whenever you find a case where an OAuth2 provider redirects back to the relying party with errors.
 data AuthorizationResponseError = AuthorizationResponseError
   { authorizationResponseError :: AuthorizationResponseErrorCode
   , authorizationResponseErrorDescription :: Maybe Text
diff --git a/src/Network/OAuth2/Experiment.hs b/src/Network/OAuth2/Experiment.hs
--- a/src/Network/OAuth2/Experiment.hs
+++ b/src/Network/OAuth2/Experiment.hs
@@ -1,32 +1,32 @@
 -- | This module contains a new way of doing OAuth2 authorization and authentication
--- in order to obtain Access Token and maybe Refresh Token base on rfc6749.
+-- in order to obtain an access token and maybe a refresh token based on RFC 6749.
 --
--- This module will become default in future release.
+-- This module will become the default in a future release.
 --
--- The key concept/change is to introduce the Grant flow, which determines the entire work flow per spec.
--- Each work flow will have slight different request parameters, which often time you'll see
--- different configuration when creating OAuth2 application in the IdP developer application page.
+-- The key concept is introducing grant flows, which determine the entire workflow per the spec.
+-- Each workflow has slightly different request parameters, which is why you'll often see
+-- different configuration options when creating an OAuth2 application in an IdP developer console.
 --
--- Here are supported flows
+-- Here are the supported flows:
 --
--- 1. Authorization Code. This flow requires authorize call to obtain an authorize code,
+-- 1. Authorization Code. This flow requires an authorize call to obtain an authorization code,
 -- then exchange the code for tokens.
 --
--- 2. Resource Owner Password. This flow only requires to hit token endpoint with, of course,
+-- 2. Resource Owner Password. This flow only requires hitting the token endpoint with, of course,
 -- username and password, to obtain tokens.
 --
--- 3. Client Credentials. This flow also only requires to hit token endpoint but with different parameters.
--- Client credentials flow does not involve an end user hence you won't be able to hit userinfo endpoint
--- with access token obtained.
+-- 3. Client Credentials. This flow also only requires hitting the token endpoint, but with different parameters.
+-- The client credentials flow does not involve an end user, so you won't be able to hit the userinfo endpoint
+-- with the access token you obtain.
 --
--- 5. PKCE (rfc7636). This is enhancement on top of authorization code flow.
+-- 4. PKCE (RFC 7636). This is an enhancement on top of the authorization code flow.
 --
--- Implicit flow is not supported because it is more for SPA (single page app)
--- given it is deprecated by Authorization Code flow with PKCE.
+-- The implicit flow is not supported because it is primarily for SPAs (single-page apps),
+-- and it has been deprecated in favor of the authorization code flow with PKCE.
 --
--- Here is quick sample for how to use vocabularies from this new module.
+-- Here is a quick sample showing how to use the vocabulary from this module.
 --
--- Firstly, initialize your IdP (use google as example) and the application.
+-- First, initialize your IdP (using Google as an example) and the application.
 --
 -- @
 --
@@ -65,27 +65,27 @@
 -- fooIdpApplication = IdpApplication fooApp googleIdp
 -- @
 --
--- Secondly, construct the authorize URL.
+-- Second, construct the authorize URL.
 --
 -- @
 -- authorizeUrl = mkAuthorizationRequest fooIdpApplication
 -- @
 --
--- Thirdly, after a successful redirect with authorize code,
--- you could exchange for access token
+-- Third, after a successful redirect with an authorization code,
+-- you can exchange it for an access token.
 --
 -- @
 -- mgr <- liftIO $ newManager tlsManagerSettings
 -- tokenResp <- conduitTokenRequest fooIdpApplication mgr authorizeCode
 -- @
 --
--- If you'd like to fetch user info, uses this method
+-- If you'd like to fetch user info, use this method:
 --
 -- @
 -- conduitUserInfoRequest fooIdpApplication mgr (accessToken tokenResp)
 -- @
 --
--- You could also find example from @hoauth2-providers-tutorials@ module.
+-- You can also find an example in the @hoauth2-providers-tutorial@ module.
 module Network.OAuth2.Experiment (
   -- * Application per Grant type
   module Network.OAuth2.Experiment.Grants,
diff --git a/src/Network/OAuth2/Experiment/Types.hs b/src/Network/OAuth2/Experiment/Types.hs
--- a/src/Network/OAuth2/Experiment/Types.hs
+++ b/src/Network/OAuth2/Experiment/Types.hs
@@ -107,7 +107,7 @@
 newtype ClientId = ClientId {unClientId :: Text}
   deriving (Show, Eq, IsString)
 
--- | Can be either "Client Secret" or JWT base on client authentication method
+-- | Can be either "Client Secret" or JWT based on the client authentication method
 newtype ClientSecret = ClientSecret {unClientSecret :: Text}
   deriving (Eq, IsString)
 
diff --git a/src/Network/OAuth2/TokenRequest.hs b/src/Network/OAuth2/TokenRequest.hs
--- a/src/Network/OAuth2/TokenRequest.hs
+++ b/src/Network/OAuth2/TokenRequest.hs
@@ -88,7 +88,7 @@
   -- ^ Exists when @offline_access@ scope is in the Authorization Request and the provider supports Refresh Access Token.
   , expiresIn :: Maybe Int
   , tokenType :: Maybe Text
-  -- ^ See https://www.rfc-editor.org/rfc/rfc6749#section-5.1. It's required per spec. But OAuth2 provider implementation are vary. Maybe will remove 'Maybe' in future release.
+  -- ^ See https://www.rfc-editor.org/rfc/rfc6749#section-5.1. It's required by spec, but OAuth2 provider implementations vary. We may remove 'Maybe' in a future release.
   , idToken :: Maybe IdToken
   -- ^ Exists when @openid@ scope is in the Authorization Request and the provider supports OpenID protocol.
   , scope :: Maybe Text
@@ -124,7 +124,7 @@
       Success a -> pure a
       Error err -> fail err
 
--- | Parse JSON data into 'OAuth2Token'
+-- | Parse JSON data into 'TokenResponse'
 instance FromJSON TokenResponse where
   parseJSON :: Value -> Parser TokenResponse
   parseJSON = withObject "TokenResponse" $ \v ->
