diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+Unreleased
+----------
+
+3.1.0
+-----
+
+- Use `format: date-time` for `ToParamSchema ZonedTime` instance (see
+  [#20](https://github.com/biocad/openapi3/pull/20)).
+- Support generating schema for any polymorphic types via `Typeable` (see
+  [#19](https://github.com/biocad/openapi3/pull/19)).
+- Allow `hashable-1.3.1`, prettify doctests (see [#18](https://github.com/biocad/openapi3/pull/18)).
+
 3.0.2.0
 -------
 
diff --git a/openapi3.cabal b/openapi3.cabal
--- a/openapi3.cabal
+++ b/openapi3.cabal
@@ -1,6 +1,6 @@
 cabal-version:       >=1.10
 name:                openapi3
-version:             3.0.2.0
+version:             3.1.0
 
 synopsis:            OpenAPI 3.0 data model
 category:            Web, Swagger, OpenApi
@@ -25,8 +25,9 @@
 tested-with:
   GHC ==8.4.4
    || ==8.6.5
-   || ==8.8.3
-   || ==8.10.1
+   || ==8.8.4
+   || ==8.10.4
+   || ==9.0.1
 
 custom-setup
   setup-depends:
@@ -57,10 +58,10 @@
 
   -- GHC boot libraries
   build-depends:
-      base             >=4.11.1.0  && <4.15
+      base             >=4.11.1.0  && <4.16
     , bytestring       >=0.10.8.2  && <0.11
     , containers       >=0.5.11.0  && <0.7
-    , template-haskell >=2.13.0.0  && <2.17
+    , template-haskell >=2.13.0.0  && <2.18
     , time             >=1.8.0.2   && <1.10
     , transformers     >=0.5.5.0   && <0.6
 
@@ -76,9 +77,7 @@
     -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint
     , cookie                    >=0.4.3    && <0.5
     , generics-sop              >=0.5.1.0  && <0.6
-      -- <1.3.1 is a temporary measure, until we fix doctests not to depend on key order
-      -- see https://github.com/haskell/aeson/issues/837
-    , hashable                  >=1.2.7.0  && <1.3.1
+    , hashable                  >=1.2.7.0  && <1.4
     , http-media                >=0.8.0.0  && <0.9
     , insert-ordered-containers >=0.2.3    && <0.3
     , lens                      >=4.16.1   && <5.1
diff --git a/src/Data/OpenApi.hs b/src/Data/OpenApi.hs
--- a/src/Data/OpenApi.hs
+++ b/src/Data/OpenApi.hs
@@ -137,6 +137,7 @@
 -- >>> import Data.Proxy
 -- >>> import GHC.Generics
 -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
+-- >>> import Data.OpenApi.Internal.Utils
 -- >>> :set -XDeriveGeneric
 -- >>> :set -XOverloadedStrings
 -- >>> :set -XOverloadedLists
@@ -153,8 +154,16 @@
 --
 -- In this library you can use @'mempty'@ for a default/empty value. For instance:
 --
--- >>> BSL.putStrLn $ encode (mempty :: OpenApi)
--- {"openapi":"3.0.0","info":{"version":"","title":""},"components":{}}
+-- >>> BSL.putStrLn $ encodePretty (mempty :: OpenApi)
+-- {
+--     "components": {},
+--     "info": {
+--         "title": "",
+--         "version": ""
+--     },
+--     "openapi": "3.0.0",
+--     "paths": {}
+-- }
 --
 -- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
 -- That is because these properties are actually required ones.
@@ -162,13 +171,20 @@
 -- You /should/ always override the default (empty) value for these properties,
 -- although it is not strictly necessary:
 --
--- >>> BSL.putStrLn $ encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
--- {"version":"1.0","title":"Todo API"}
+-- >>> BSL.putStrLn $ encodePretty mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
+-- {
+--     "title": "Todo API",
+--     "version": "1.0"
+-- }
 --
 -- You can merge two values using @'mappend'@ or its infix version @('<>')@:
 --
--- >>> BSL.putStrLn $ encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
--- {"version":"1.0","title":"Todo API"}
+-- >>> BSL.putStrLn $ encodePretty $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
+-- {
+--     "title": "Todo API",
+--     "version": "1.0"
+-- }
+
 --
 -- This can be useful for combining specifications of endpoints into a whole API specification:
 --
@@ -194,14 +210,48 @@
 -- make it fairly simple to construct/modify any part of the specification:
 --
 -- >>> :{
--- BSL.putStrLn $ encode $ (mempty :: OpenApi)
+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
 --   & components . schemas .~ [ ("User", mempty & type_ ?~ OpenApiString) ]
 --   & paths .~
 --     [ ("/user", mempty & get ?~ (mempty
 --         & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User")))
 --         & at 404 ?~ "User info not found")) ]
 -- :}
--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"404":{"description":"User info not found"},"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"OK"}}}}},"components":{"schemas":{"User":{"type":"string"}}}}
+-- {
+--     "components": {
+--         "schemas": {
+--             "User": {
+--                 "type": "string"
+--             }
+--         }
+--     },
+--     "info": {
+--         "title": "",
+--         "version": ""
+--     },
+--     "openapi": "3.0.0",
+--     "paths": {
+--         "/user": {
+--             "get": {
+--                 "responses": {
+--                     "200": {
+--                         "content": {
+--                             "application/json": {
+--                                 "schema": {
+--                                     "$ref": "#/components/schemas/User"
+--                                 }
+--                             }
+--                         },
+--                         "description": "OK"
+--                     },
+--                     "404": {
+--                         "description": "User info not found"
+--                     }
+--                 }
+--             }
+--         }
+--     }
+-- }
 --
 -- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
 -- which produces @application/json@ output. It should respond with code @200@ and body specified
@@ -213,23 +263,34 @@
 -- common field is @'description'@. Many components of a Swagger specification
 -- can have descriptions, and you can use the same name for them:
 --
--- >>> BSL.putStrLn $ encode $ (mempty :: Response) & description .~ "No content"
--- {"description":"No content"}
+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & description .~ "No content"
+-- {
+--     "description": "No content"
+-- }
 -- >>> :{
--- BSL.putStrLn $ encode $ (mempty :: Schema)
+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
 --   & type_       ?~ OpenApiBoolean
 --   & description ?~ "To be or not to be"
 -- :}
--- {"type":"boolean","description":"To be or not to be"}
+-- {
+--     "description": "To be or not to be",
+--     "type": "boolean"
+-- }
 --
 -- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
 -- have direct access to it via @'at' code@. Example:
 --
 -- >>> :{
--- BSL.putStrLn $ encode $ (mempty :: Operation)
+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
 --   & at 404 ?~ "Not found"
 -- :}
--- {"responses":{"404":{"description":"Not found"}}}
+-- {
+--     "responses": {
+--         "404": {
+--             "description": "Not found"
+--         }
+--     }
+-- }
 --
 -- You might've noticed that @'type_'@ has an extra underscore in its name
 -- compared to, say, @'description'@ field accessor.
@@ -276,10 +337,27 @@
 -- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
 -- >>> instance ToJSON Person
 -- >>> instance ToSchema Person
--- >>> BSL.putStrLn $ encode (Person "David" 28)
--- {"age":28,"name":"David"}
--- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Person)
--- {"required":["name","age"],"type":"object","properties":{"age":{"type":"integer"},"name":{"type":"string"}}}
+-- >>> BSL.putStrLn $ encodePretty (Person "David" 28)
+-- {
+--     "age": 28,
+--     "name": "David"
+-- }
+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Person)
+-- {
+--     "properties": {
+--         "age": {
+--             "type": "integer"
+--         },
+--         "name": {
+--             "type": "string"
+--         }
+--     },
+--     "required": [
+--         "name",
+--         "age"
+--     ],
+--     "type": "object"
+-- }
 --
 -- This package implements OpenAPI 3.0 spec, which supports @oneOf@ in schemas, allowing any sum types
 -- to be faithfully represented. All sum encodings supported by @aeson@ are supported here as well, with
@@ -290,8 +368,50 @@
 -- >>> data Error = ErrorNoUser { userId :: Int } | ErrorAccessDenied { requiredPermission :: String } deriving Generic
 -- >>> instance ToJSON Error
 -- >>> instance ToSchema Error
--- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Error)
--- {"oneOf":[{"required":["userId","tag"],"type":"object","properties":{"tag":{"type":"string","enum":["ErrorNoUser"]},"userId":{"maximum":9223372036854775807,"minimum":-9223372036854775808,"type":"integer"}}},{"required":["requiredPermission","tag"],"type":"object","properties":{"tag":{"type":"string","enum":["ErrorAccessDenied"]},"requiredPermission":{"type":"string"}}}],"type":"object"}
+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Error)
+-- {
+--     "oneOf": [
+--         {
+--             "properties": {
+--                 "tag": {
+--                     "enum": [
+--                         "ErrorNoUser"
+--                     ],
+--                     "type": "string"
+--                 },
+--                 "userId": {
+--                     "maximum": 9223372036854775807,
+--                     "minimum": -9223372036854775808,
+--                     "type": "integer"
+--                 }
+--             },
+--             "required": [
+--                 "userId",
+--                 "tag"
+--             ],
+--             "type": "object"
+--         },
+--         {
+--             "properties": {
+--                 "requiredPermission": {
+--                     "type": "string"
+--                 },
+--                 "tag": {
+--                     "enum": [
+--                         "ErrorAccessDenied"
+--                     ],
+--                     "type": "string"
+--                 }
+--             },
+--             "required": [
+--                 "requiredPermission",
+--                 "tag"
+--             ],
+--             "type": "object"
+--         }
+--     ],
+--     "type": "object"
+-- }
 
 -- $manipulation
 -- Sometimes you have to work with an imported or generated @'Swagger'@.
diff --git a/src/Data/OpenApi/Internal.hs b/src/Data/OpenApi/Internal.hs
--- a/src/Data/OpenApi/Internal.hs
+++ b/src/Data/OpenApi/Internal.hs
@@ -60,6 +60,8 @@
 -- $setup
 -- >>> :seti -XDataKinds
 -- >>> import Data.Aeson
+-- >>> import Data.ByteString.Lazy.Char8 as BSL
+-- >>> import Data.OpenApi.Internal.Utils
 
 -- | A list of definitions that can be used in references.
 type Definitions = InsOrdHashMap Text
@@ -848,20 +850,37 @@
 
 -- |
 --
--- >>> encode (SecuritySchemeHttp (HttpSchemeBearer Nothing))
--- "{\"scheme\":\"bearer\",\"type\":\"http\"}"
+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer Nothing))
+-- {
+--     "scheme": "bearer",
+--     "type": "http"
+-- }
 --
--- >>> encode (SecuritySchemeHttp (HttpSchemeBearer (Just "jwt")))
--- "{\"scheme\":\"bearer\",\"type\":\"http\",\"bearerFormat\":\"jwt\"}"
+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer (Just "jwt")))
+-- {
+--     "bearerFormat": "jwt",
+--     "scheme": "bearer",
+--     "type": "http"
+-- }
 --
--- >>> encode (SecuritySchemeHttp HttpSchemeBasic)
--- "{\"scheme\":\"basic\",\"type\":\"http\"}"
+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp HttpSchemeBasic)
+-- {
+--     "scheme": "basic",
+--     "type": "http"
+-- }
 --
--- >>> encode (SecuritySchemeHttp (HttpSchemeCustom "CANARY"))
--- "{\"scheme\":\"CANARY\",\"type\":\"http\"}"
+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeCustom "CANARY"))
+-- {
+--     "scheme": "CANARY",
+--     "type": "http"
+-- }
 --
--- >>> encode (SecuritySchemeApiKey (ApiKeyParams "id" ApiKeyCookie))
--- "{\"in\":\"cookie\",\"name\":\"id\",\"type\":\"apiKey\"}"
+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeApiKey (ApiKeyParams "id" ApiKeyCookie))
+-- {
+--     "in": "cookie",
+--     "name": "id",
+--     "type": "apiKey"
+-- }
 --
 data SecuritySchemeType
   = SecuritySchemeHttp HttpSchemeType
@@ -1305,8 +1324,12 @@
 -- | As for nullary schema for 0-arity type constructors, see
 -- <https://github.com/GetShopTV/swagger2/issues/167>.
 --
--- >>> encode (OpenApiItemsArray [])
--- "{\"example\":[],\"items\":{},\"maxItems\":0}"
+-- >>> BSL.putStrLn $ encodePretty (OpenApiItemsArray [])
+-- {
+--     "example": [],
+--     "items": {},
+--     "maxItems": 0
+-- }
 --
 instance ToJSON OpenApiItems where
   toJSON (OpenApiItemsObject x) = object [ "items" .= x ]
diff --git a/src/Data/OpenApi/Internal/ParamSchema.hs b/src/Data/OpenApi/Internal/ParamSchema.hs
--- a/src/Data/OpenApi/Internal/ParamSchema.hs
+++ b/src/Data/OpenApi/Internal/ParamSchema.hs
@@ -46,7 +46,7 @@
 import Data.OpenApi.SchemaOptions
 
 import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as BSL
+import qualified Data.ByteString.Lazy.Char8 as BSL
 import GHC.TypeLits (TypeError, ErrorMessage(..))
 
 -- | Default schema for binary data (any sequence of octets).
@@ -112,8 +112,10 @@
 class ToParamSchema a where
   -- | Convert a type into a plain parameter schema.
   --
-  -- >>> encode $ toParamSchema (Proxy :: Proxy Integer)
-  -- "{\"type\":\"integer\"}"
+  -- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy Integer)
+  -- {
+  --     "type": "integer"
+  -- }
   toParamSchema :: Proxy a -> Schema
   default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => Proxy a -> Schema
   toParamSchema = genericToParamSchema defaultSchemaOptions
@@ -155,8 +157,12 @@
 
 -- | Default plain schema for @'Bounded'@, @'Integral'@ types.
 --
--- >>> encode $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)
--- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
+-- >>> BSL.putStrLn $ encodePretty $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)
+-- {
+--     "maximum": 127,
+--     "minimum": -128,
+--     "type": "integer"
+-- }
 toParamSchemaBoundedIntegral :: forall a t. (Bounded a, Integral a) => Proxy a -> Schema
 toParamSchemaBoundedIntegral _ = mempty
   & type_ ?~ OpenApiInteger
@@ -210,9 +216,9 @@
 
 -- |
 -- >>> toParamSchema (Proxy :: Proxy ZonedTime) ^. format
--- Just "yyyy-mm-ddThh:MM:ss+hhMM"
+-- Just "date-time"
 instance ToParamSchema ZonedTime where
-  toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ss+hhMM"
+  toParamSchema _ = timeParamSchema "date-time"
 
 -- |
 -- >>> toParamSchema (Proxy :: Proxy UTCTime) ^. format
@@ -275,8 +281,13 @@
   toParamSchema _ = toParamSchema (Proxy :: Proxy (Set a))
 
 -- |
--- >>> encode $ toParamSchema (Proxy :: Proxy ())
--- "{\"type\":\"string\",\"enum\":[\"_\"]}"
+-- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy ())
+-- {
+--     "enum": [
+--         "_"
+--     ],
+--     "type": "string"
+-- }
 instance ToParamSchema () where
   toParamSchema _ = mempty
     & type_ ?~ OpenApiString
@@ -291,8 +302,14 @@
 --
 -- >>> :set -XDeriveGeneric
 -- >>> data Color = Red | Blue deriving Generic
--- >>> encode $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)
--- "{\"type\":\"string\",\"enum\":[\"Red\",\"Blue\"]}"
+-- >>> BSL.putStrLn $ encodePretty $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)
+-- {
+--     "enum": [
+--         "Red",
+--         "Blue"
+--     ],
+--     "type": "string"
+-- }
 genericToParamSchema :: forall a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> Proxy a -> Schema
 genericToParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy (Rep a)) mempty
 
@@ -334,3 +351,4 @@
 
 -- $setup
 -- >>> import Data.Aeson (encode)
+-- >>> import Data.OpenApi.Internal.Utils
diff --git a/src/Data/OpenApi/Internal/Schema.hs b/src/Data/OpenApi/Internal/Schema.hs
--- a/src/Data/OpenApi/Internal/Schema.hs
+++ b/src/Data/OpenApi/Internal/Schema.hs
@@ -42,6 +42,7 @@
 import Data.Int
 import Data.IntSet (IntSet)
 import Data.IntMap (IntMap)
+import Data.List (sort)
 import Data.List.NonEmpty.Compat (NonEmpty)
 import Data.Map (Map)
 import Data.Maybe (fromMaybe)
@@ -134,7 +135,7 @@
 --
 -- instance ToSchema Coord
 -- @
-class ToSchema a where
+class Typeable a => ToSchema a where
   -- | Convert a type into an optionally named schema
   -- together with all used definitions.
   -- Note that the schema itself is included in definitions
@@ -156,13 +157,19 @@
 --
 -- >>> toNamedSchema (Proxy :: Proxy String) ^. name
 -- Nothing
--- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy String) ^. schema)
--- {"type":"string"}
+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy String) ^. schema)
+-- {
+--     "type": "string"
+-- }
 --
 -- >>> toNamedSchema (Proxy :: Proxy Day) ^. name
 -- Just "Day"
--- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)
--- {"example":"2016-07-22","format":"date","type":"string"}
+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy Day) ^. schema)
+-- {
+--     "example": "2016-07-22",
+--     "format": "date",
+--     "type": "string"
+-- }
 toNamedSchema :: ToSchema a => Proxy a -> NamedSchema
 toNamedSchema = undeclare . declareNamedSchema
 
@@ -178,22 +185,35 @@
 
 -- | Convert a type into a schema.
 --
--- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Int8)
--- {"maximum":127,"minimum":-128,"type":"integer"}
+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Int8)
+-- {
+--     "maximum": 127,
+--     "minimum": -128,
+--     "type": "integer"
+-- }
 --
--- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy [Day])
--- {"items":{"$ref":"#/components/schemas/Day"},"type":"array"}
+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy [Day])
+-- {
+--     "items": {
+--         "$ref": "#/components/schemas/Day"
+--     },
+--     "type": "array"
+-- }
 toSchema :: ToSchema a => Proxy a -> Schema
 toSchema = _namedSchemaSchema . toNamedSchema
 
 -- | Convert a type into a referenced schema if possible.
 -- Only named schemas can be referenced, nameless schemas are inlined.
 --
--- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Integer)
--- {"type":"integer"}
+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Integer)
+-- {
+--     "type": "integer"
+-- }
 --
--- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Day)
--- {"$ref":"#/components/schemas/Day"}
+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Day)
+-- {
+--     "$ref": "#/components/schemas/Day"
+-- }
 toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema
 toSchemaRef = undeclare . declareSchemaRef
 
@@ -261,8 +281,15 @@
 
 -- | Convert a type into a schema without references.
 --
--- >>> BSL.putStrLn $ encode $ toInlinedSchema (Proxy :: Proxy [Day])
--- {"items":{"example":"2016-07-22","format":"date","type":"string"},"type":"array"}
+-- >>> BSL.putStrLn $ encodePretty $ toInlinedSchema (Proxy :: Proxy [Day])
+-- {
+--     "items": {
+--         "example": "2016-07-22",
+--         "format": "date",
+--         "type": "string"
+--     },
+--     "type": "array"
+-- }
 --
 -- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema
 -- when inlining recursive schemas.
@@ -295,19 +322,64 @@
 -- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
 -- Produced schema can be used for further refinement.
 --
--- >>> BSL.putStrLn $ encode $ sketchSchema "hello"
--- {"example":"hello","type":"string"}
+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema "hello"
+-- {
+--     "example": "hello",
+--     "type": "string"
+-- }
 --
--- >>> BSL.putStrLn $ encode $ sketchSchema (1, 2, 3)
--- {"example":[1,2,3],"items":{"type":"number"},"type":"array"}
+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (1, 2, 3)
+-- {
+--     "example": [
+--         1,
+--         2,
+--         3
+--     ],
+--     "items": {
+--         "type": "number"
+--     },
+--     "type": "array"
+-- }
 --
--- >>> BSL.putStrLn $ encode $ sketchSchema ("Jack", 25)
--- {"example":["Jack",25],"items":[{"type":"string"},{"type":"number"}],"type":"array"}
+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema ("Jack", 25)
+-- {
+--     "example": [
+--         "Jack",
+--         25
+--     ],
+--     "items": [
+--         {
+--             "type": "string"
+--         },
+--         {
+--             "type": "number"
+--         }
+--     ],
+--     "type": "array"
+-- }
 --
 -- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
 -- >>> instance ToJSON Person
--- >>> BSL.putStrLn $ encode $ sketchSchema (Person "Jack" 25)
--- {"example":{"age":25,"name":"Jack"},"required":["age","name"],"type":"object","properties":{"age":{"type":"number"},"name":{"type":"string"}}}
+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (Person "Jack" 25)
+-- {
+--     "example": {
+--         "age": 25,
+--         "name": "Jack"
+--     },
+--     "properties": {
+--         "age": {
+--             "type": "number"
+--         },
+--         "name": {
+--             "type": "string"
+--         }
+--     },
+--     "required": [
+--         "age",
+--         "name"
+--     ],
+--     "type": "object"
+-- }
 sketchSchema :: ToJSON a => a -> Schema
 sketchSchema = sketch . toJSON
   where
@@ -333,25 +405,139 @@
           _               -> Nothing
     go (Object o) = mempty
       & type_         ?~ OpenApiObject
-      & required      .~ HashMap.keys o
+      & required      .~ sort (HashMap.keys o)
       & properties    .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
 
 -- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
 -- Produced schema uses as much constraints as possible.
 --
--- >>> BSL.putStrLn $ encode $ sketchStrictSchema "hello"
--- {"maxLength":5,"pattern":"hello","minLength":5,"type":"string","enum":["hello"]}
+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema "hello"
+-- {
+--     "enum": [
+--         "hello"
+--     ],
+--     "maxLength": 5,
+--     "minLength": 5,
+--     "pattern": "hello",
+--     "type": "string"
+-- }
 --
--- >>> BSL.putStrLn $ encode $ sketchStrictSchema (1, 2, 3)
--- {"minItems":3,"uniqueItems":true,"items":[{"maximum":1,"minimum":1,"multipleOf":1,"type":"number","enum":[1]},{"maximum":2,"minimum":2,"multipleOf":2,"type":"number","enum":[2]},{"maximum":3,"minimum":3,"multipleOf":3,"type":"number","enum":[3]}],"maxItems":3,"type":"array","enum":[[1,2,3]]}
+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (1, 2, 3)
+-- {
+--     "enum": [
+--         [
+--             1,
+--             2,
+--             3
+--         ]
+--     ],
+--     "items": [
+--         {
+--             "enum": [
+--                 1
+--             ],
+--             "maximum": 1,
+--             "minimum": 1,
+--             "multipleOf": 1,
+--             "type": "number"
+--         },
+--         {
+--             "enum": [
+--                 2
+--             ],
+--             "maximum": 2,
+--             "minimum": 2,
+--             "multipleOf": 2,
+--             "type": "number"
+--         },
+--         {
+--             "enum": [
+--                 3
+--             ],
+--             "maximum": 3,
+--             "minimum": 3,
+--             "multipleOf": 3,
+--             "type": "number"
+--         }
+--     ],
+--     "maxItems": 3,
+--     "minItems": 3,
+--     "type": "array",
+--     "uniqueItems": true
+-- }
 --
--- >>> BSL.putStrLn $ encode $ sketchStrictSchema ("Jack", 25)
--- {"minItems":2,"uniqueItems":true,"items":[{"maxLength":4,"pattern":"Jack","minLength":4,"type":"string","enum":["Jack"]},{"maximum":25,"minimum":25,"multipleOf":25,"type":"number","enum":[25]}],"maxItems":2,"type":"array","enum":[["Jack",25]]}
+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema ("Jack", 25)
+-- {
+--     "enum": [
+--         [
+--             "Jack",
+--             25
+--         ]
+--     ],
+--     "items": [
+--         {
+--             "enum": [
+--                 "Jack"
+--             ],
+--             "maxLength": 4,
+--             "minLength": 4,
+--             "pattern": "Jack",
+--             "type": "string"
+--         },
+--         {
+--             "enum": [
+--                 25
+--             ],
+--             "maximum": 25,
+--             "minimum": 25,
+--             "multipleOf": 25,
+--             "type": "number"
+--         }
+--     ],
+--     "maxItems": 2,
+--     "minItems": 2,
+--     "type": "array",
+--     "uniqueItems": true
+-- }
 --
 -- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
 -- >>> instance ToJSON Person
--- >>> BSL.putStrLn $ encode $ sketchStrictSchema (Person "Jack" 25)
--- {"minProperties":2,"required":["age","name"],"maxProperties":2,"type":"object","enum":[{"age":25,"name":"Jack"}],"properties":{"age":{"maximum":25,"minimum":25,"multipleOf":25,"type":"number","enum":[25]},"name":{"maxLength":4,"pattern":"Jack","minLength":4,"type":"string","enum":["Jack"]}}}
+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (Person "Jack" 25)
+-- {
+--     "enum": [
+--         {
+--             "age": 25,
+--             "name": "Jack"
+--         }
+--     ],
+--     "maxProperties": 2,
+--     "minProperties": 2,
+--     "properties": {
+--         "age": {
+--             "enum": [
+--                 25
+--             ],
+--             "maximum": 25,
+--             "minimum": 25,
+--             "multipleOf": 25,
+--             "type": "number"
+--         },
+--         "name": {
+--             "enum": [
+--                 "Jack"
+--             ],
+--             "maxLength": 4,
+--             "minLength": 4,
+--             "pattern": "Jack",
+--             "type": "string"
+--         }
+--     },
+--     "required": [
+--         "age",
+--         "name"
+--     ],
+--     "type": "object"
+-- }
 sketchStrictSchema :: ToJSON a => a -> Schema
 sketchStrictSchema = go . toJSON
   where
@@ -383,7 +569,7 @@
         allUnique = sz == HashSet.size (HashSet.fromList (V.toList xs))
     go js@(Object o) = mempty
       & type_         ?~ OpenApiObject
-      & required      .~ names
+      & required      .~ sort names
       & properties    .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
       & maxProperties ?~ fromIntegral (length names)
       & minProperties ?~ fromIntegral (length names)
@@ -424,7 +610,7 @@
 instance ToSchema Double      where declareNamedSchema = plain . paramSchemaToSchema
 instance ToSchema Float       where declareNamedSchema = plain . paramSchemaToSchema
 
-instance HasResolution a => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
+instance (Typeable (Fixed a), HasResolution a) => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
 
 instance ToSchema a => ToSchema (Maybe a) where
   declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)
@@ -441,12 +627,18 @@
   declareNamedSchema p = pure $ named "UUID" $ paramSchemaToSchema p
     & example ?~ toJSON (UUID.toText UUID.nil)
 
-instance (ToSchema a, ToSchema b) => ToSchema (a, b)
-instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c)
-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d)
-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e)
-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f)
-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g)
+instance (ToSchema a, ToSchema b) => ToSchema (a, b) where
+  declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
+instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c) where
+  declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d) where
+  declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e) where
+  declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f) where
+  declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g) where
+  declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
 
 timeSchema :: T.Text -> Schema
 timeSchema fmt = mempty
@@ -465,7 +657,7 @@
   declareNamedSchema _ = pure $ named "LocalTime" $ timeSchema "yyyy-mm-ddThh:MM:ss"
     & example ?~ toJSON (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0))
 
--- | Format @"date"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
+-- | Format @"date-time"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
 instance ToSchema ZonedTime where
   declareNamedSchema _ = pure $ named "ZonedTime" $ timeSchema "date-time"
     & example ?~ toJSON (ZonedTime (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) (hoursToTimeZone 3))
@@ -497,7 +689,7 @@
 instance ToSchema IntSet where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Set Int))
 
 -- | NOTE: This schema does not account for the uniqueness of keys.
-instance ToSchema a => ToSchema (IntMap a) where
+instance (ToSchema a) => ToSchema (IntMap a) where
   declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)])
 
 instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
@@ -553,8 +745,12 @@
 
 -- | Default schema for @'Bounded'@, @'Integral'@ types.
 --
--- >>> BSL.putStrLn $ encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
--- {"maximum":32767,"minimum":-32768,"type":"integer"}
+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
+-- {
+--     "maximum": 32767,
+--     "minimum": -32768,
+--     "type": "integer"
+-- }
 toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema
 toSchemaBoundedIntegral _ = mempty
   & type_ ?~ OpenApiInteger
@@ -586,8 +782,27 @@
 -- >>> instance ToSchema ButtonState
 -- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
 -- >>> type ImageUrl = T.Text
--- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
--- {"type":"object","properties":{"Focus":{"type":"string"},"Disabled":{"type":"string"},"Active":{"type":"string"},"Neutral":{"type":"string"},"Hover":{"type":"string"}}}
+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
+-- {
+--     "properties": {
+--         "Active": {
+--             "type": "string"
+--         },
+--         "Disabled": {
+--             "type": "string"
+--         },
+--         "Focus": {
+--             "type": "string"
+--         },
+--         "Hover": {
+--             "type": "string"
+--         },
+--         "Neutral": {
+--             "type": "string"
+--         }
+--     },
+--     "type": "object"
+-- }
 --
 -- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
 -- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -614,8 +829,27 @@
 -- >>> instance ToSchema ButtonState
 -- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
 -- >>> type ImageUrl = T.Text
--- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
--- {"type":"object","properties":{"Focus":{"type":"string"},"Disabled":{"type":"string"},"Active":{"type":"string"},"Neutral":{"type":"string"},"Hover":{"type":"string"}}}
+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
+-- {
+--     "properties": {
+--         "Active": {
+--             "type": "string"
+--         },
+--         "Disabled": {
+--             "type": "string"
+--         },
+--         "Focus": {
+--             "type": "string"
+--         },
+--         "Hover": {
+--             "type": "string"
+--         },
+--         "Neutral": {
+--             "type": "string"
+--         }
+--     },
+--     "type": "object"
+-- }
 --
 -- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
 -- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -625,7 +859,7 @@
 toSchemaBoundedEnumKeyMapping = flip evalDeclare mempty . declareSchemaBoundedEnumKeyMapping
 
 -- | A configurable generic @'Schema'@ creator.
-genericDeclareSchema :: (Generic a, GToSchema (Rep a)) =>
+genericDeclareSchema :: (Generic a, GToSchema (Rep a), Typeable a) =>
   SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
 genericDeclareSchema opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchema opts proxy
 
@@ -633,10 +867,25 @@
 -- This function applied to @'defaultSchemaOptions'@
 -- is used as the default for @'declareNamedSchema'@
 -- when the type is an instance of @'Generic'@.
-genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a)) =>
+--
+-- Default implementation will use the name from 'Typeable' instance, including concrete
+-- instantioations of type variables.
+--
+-- For example:
+--
+-- >>> _namedSchemaName $ undeclare $ genericDeclareNamedSchema defaultSchemaOptions (Proxy :: Proxy (Either Int Bool))
+-- Just "Either_Int_Bool"
+genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a), Typeable a) =>
   SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
-genericDeclareNamedSchema opts _ = gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
+genericDeclareNamedSchema opts _ =
+  rename (Just $ T.pack name) <$> gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
+  where
+    unspace ' ' = '_'
+    unspace x = x
+    orig = fmap unspace $ show $ typeRep @a
+    name = datatypeNameModifier opts orig
 
+
 -- | Derive a 'Generic'-based name for a datatype and assign it to a given 'Schema'.
 genericNameSchema :: forall a d f.
   (Generic a, Rep a ~ D1 d f, Datatype d)
@@ -861,58 +1110,13 @@
 
 data Proxy3 a b c = Proxy3
 
--- | This class allows to generate schemas for polymorphic types (of kind @Type -> Type@).
---
--- Intended usage:
---
--- >>> data Foo a = Foo { foo :: a, bar :: Int } deriving (Eq, Show, Generic, ToSchema1)
--- >>> :{
--- instance (ToSchema a, Typeable a) => ToSchema (Foo a) where
---   declareNamedSchema _ = declareNamedSchema @(BySchema1 Foo a) Proxy
--- :}
---
--- >>> toNamedSchema @(Foo Int) Proxy ^. name
--- Just "Foo_Int"
--- >>> toNamedSchema @(Foo Bool) Proxy ^. name
--- Just "Foo_Bool"
--- >>> toNamedSchema @(Foo (Foo T.Text)) Proxy ^. name
--- Just "Foo_(Foo_Text)"
-class ToSchema1 (f :: * -> *) where
-  declareNamedSchema1 :: (Generic (f a), GToSchema (Rep (f a)), ToSchema a) => Proxy f -> Proxy a -> Declare (Definitions Schema) NamedSchema
-
-  -- It would be cleaner to have GToSchema constraint only on default signature and not in the class method
-  -- above, however sadly GHC does not like it.
-  default declareNamedSchema1 :: forall a. (ToSchema a, Generic (f a), GToSchema (Rep (f a))) => Proxy f -> Proxy a -> Declare (Definitions Schema) NamedSchema
-  declareNamedSchema1 _ _ = genericDeclareNamedSchema @(f a) defaultSchemaOptions Proxy
-
-{- | For GHC 8.6+ it's more convenient to use @DerivingVia@ to derive instances of 'ToSchema'
-using 'ToSchema1' instance, like this:
-
-#if __GLASGOW_HASKELL__ >= 806
->>> data Foo a = Foo { foo :: a, bar :: Int } deriving (Eq, Show, Generic, ToSchema1)
->>> deriving via BySchema1 Foo a instance (ToSchema a, Typeable a) => ToSchema (Foo a)
-#else
-> data Foo a = Foo { foo :: a, bar :: Int } deriving (Eq, Show, Generic, ToSchema1)
-> deriving via BySchema1 Foo a instance (ToSchema a, Typeable a) => ToSchema (Foo a)
-#endif
--}
-newtype BySchema1 f a = BySchema1 (f a)
-
-instance (ToSchema1 f, Generic (f a), GToSchema (Rep (f a)), Typeable (f a), ToSchema a) => ToSchema (BySchema1 f a) where
-  declareNamedSchema _ = do
-    sch <- declareNamedSchema1 @f @a Proxy Proxy
-    let tName = T.replace " " "_" $ T.pack $ show $ typeRep @(f a)
-    return $ rename (Just tName) sch
-
 {- $setup
 >>> import Data.OpenApi
 >>> import Data.Aeson (encode)
 >>> import Data.Aeson.Types (toJSONKeyText)
+>>> import Data.OpenApi.Internal.Utils
 >>> :set -XScopedTypeVariables
 >>> :set -XDeriveAnyClass
 >>> :set -XStandaloneDeriving
 >>> :set -XTypeApplications
-#if __GLASGOW_HASKELL__ >= 806
->>> :set -XDerivingVia
-#endif
 -}
diff --git a/src/Data/OpenApi/Internal/Schema/Validation.hs b/src/Data/OpenApi/Internal/Schema/Validation.hs
--- a/src/Data/OpenApi/Internal/Schema/Validation.hs
+++ b/src/Data/OpenApi/Internal/Schema/Validation.hs
@@ -31,7 +31,6 @@
 import           Control.Monad                       (forM, forM_, when)
 
 import           Data.Aeson                          hiding (Result)
-import           Data.Aeson.Encode.Pretty            (encodePretty)
 import           Data.Foldable                       (for_, sequenceA_,
                                                       traverse_)
 import           Data.HashMap.Strict                 (HashMap)
@@ -50,6 +49,7 @@
 
 import           Data.OpenApi.Declare
 import           Data.OpenApi.Internal
+import           Data.OpenApi.Internal.Utils
 import           Data.OpenApi.Internal.Schema
 import           Data.OpenApi.Lens
 
@@ -103,33 +103,33 @@
 -- <BLANKLINE>
 -- Swagger Schema:
 -- {
---     "required": [
---         "name",
---         "phone"
---     ],
---     "type": "object",
 --     "properties": {
---         "phone": {
---             "$ref": "#/components/schemas/Phone"
---         },
 --         "name": {
 --             "type": "string"
+--         },
+--         "phone": {
+--             "$ref": "#/components/schemas/Phone"
 --         }
---     }
+--     },
+--     "required": [
+--         "name",
+--         "phone"
+--     ],
+--     "type": "object"
 -- }
 -- <BLANKLINE>
 -- Swagger Description Context:
 -- {
 --     "Phone": {
---         "required": [
---             "value"
---         ],
---         "type": "object",
 --         "properties": {
 --             "value": {
 --                 "type": "string"
 --             }
---         }
+--         },
+--         "required": [
+--             "value"
+--         ],
+--         "type": "object"
 --     }
 -- }
 -- <BLANKLINE>
diff --git a/src/Data/OpenApi/Internal/Utils.hs b/src/Data/OpenApi/Internal/Utils.hs
--- a/src/Data/OpenApi/Internal/Utils.hs
+++ b/src/Data/OpenApi/Internal/Utils.hs
@@ -13,6 +13,8 @@
 import Control.Lens.TH
 import Data.Aeson
 import Data.Aeson.Types
+import qualified Data.Aeson.Encode.Pretty as P
+import qualified Data.ByteString.Lazy as BSL
 import Data.Char
 import Data.Data
 import Data.Hashable (Hashable)
@@ -132,3 +134,6 @@
   swaggerMempty = Nothing
   swaggerMappend x Nothing = x
   swaggerMappend _ y = y
+
+encodePretty :: ToJSON a => a -> BSL.ByteString
+encodePretty = P.encodePretty' $ P.defConfig { P.confCompare = P.compare }
diff --git a/src/Data/OpenApi/Operation.hs b/src/Data/OpenApi/Operation.hs
--- a/src/Data/OpenApi/Operation.hs
+++ b/src/Data/OpenApi/Operation.hs
@@ -55,13 +55,16 @@
 -- >>> import Data.Proxy
 -- >>> import Data.Time
 -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
+-- >>> import Data.OpenApi.Internal.Utils
 
 -- | Prepend path piece to all operations of the spec.
 -- Leading and trailing slashes are trimmed/added automatically.
 --
 -- >>> let api = (mempty :: OpenApi) & paths .~ [("/info", mempty)]
--- >>> BSL.putStrLn $ encode $ prependPath "user/{user_id}" api ^. paths
--- {"/user/{user_id}/info":{}}
+-- >>> BSL.putStrLn $ encodePretty $ prependPath "user/{user_id}" api ^. paths
+-- {
+--     "/user/{user_id}/info": {}
+-- }
 prependPath :: FilePath -> OpenApi -> OpenApi
 prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
   where
@@ -82,10 +85,63 @@
 -- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK"
 -- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]
 -- >>> let sub = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]
--- >>> BSL.putStrLn $ encode api
--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"200":{"description":"OK"}}},"post":{"responses":{"200":{"description":"OK"}}}}},"components":{}}
--- >>> BSL.putStrLn $ encode $ api & operationsOf sub . at 404 ?~ "Not found"
--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"404":{"description":"Not found"},"200":{"description":"OK"}}},"post":{"responses":{"200":{"description":"OK"}}}}},"components":{}}
+-- >>> BSL.putStrLn $ encodePretty api
+-- {
+--     "components": {},
+--     "info": {
+--         "title": "",
+--         "version": ""
+--     },
+--     "openapi": "3.0.0",
+--     "paths": {
+--         "/user": {
+--             "get": {
+--                 "responses": {
+--                     "200": {
+--                         "description": "OK"
+--                     }
+--                 }
+--             },
+--             "post": {
+--                 "responses": {
+--                     "200": {
+--                         "description": "OK"
+--                     }
+--                 }
+--             }
+--         }
+--     }
+-- }
+-- >>> BSL.putStrLn $ encodePretty $ api & operationsOf sub . at 404 ?~ "Not found"
+-- {
+--     "components": {},
+--     "info": {
+--         "title": "",
+--         "version": ""
+--     },
+--     "openapi": "3.0.0",
+--     "paths": {
+--         "/user": {
+--             "get": {
+--                 "responses": {
+--                     "200": {
+--                         "description": "OK"
+--                     },
+--                     "404": {
+--                         "description": "Not found"
+--                     }
+--                 }
+--             },
+--             "post": {
+--                 "responses": {
+--                     "200": {
+--                         "description": "OK"
+--                     }
+--                 }
+--             }
+--         }
+--     }
+-- }
 operationsOf :: OpenApi -> Traversal' OpenApi Operation
 operationsOf sub = paths.itraversed.withIndex.subops
   where
@@ -123,8 +179,26 @@
 --
 -- FIXME doc
 --
--- >>> BSL.putStrLn $ encode $ runDeclare (declareResponse "application/json" (Proxy :: Proxy Day)) mempty
--- [{"Day":{"example":"2016-07-22","format":"date","type":"string"}},{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Day"}}}}]
+-- >>> BSL.putStrLn $ encodePretty $ runDeclare (declareResponse "application/json" (Proxy :: Proxy Day)) mempty
+-- [
+--     {
+--         "Day": {
+--             "example": "2016-07-22",
+--             "format": "date",
+--             "type": "string"
+--         }
+--     },
+--     {
+--         "content": {
+--             "application/json": {
+--                 "schema": {
+--                     "$ref": "#/components/schemas/Day"
+--                 }
+--             }
+--         },
+--         "description": ""
+--     }
+-- ]
 declareResponse :: ToSchema a => MediaType -> Proxy a -> Declare (Definitions Schema) Response
 declareResponse cType proxy = do
   s <- declareSchemaRef proxy
@@ -143,8 +217,41 @@
 --
 -- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]
 -- >>> let res = declareResponse "application/json" (Proxy :: Proxy Day)
--- >>> BSL.putStrLn $ encode $ api & setResponse 200 res
--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Day"}}},"description":""}}}}},"components":{"schemas":{"Day":{"example":"2016-07-22","format":"date","type":"string"}}}}
+-- >>> BSL.putStrLn $ encodePretty $ api & setResponse 200 res
+-- {
+--     "components": {
+--         "schemas": {
+--             "Day": {
+--                 "example": "2016-07-22",
+--                 "format": "date",
+--                 "type": "string"
+--             }
+--         }
+--     },
+--     "info": {
+--         "title": "",
+--         "version": ""
+--     },
+--     "openapi": "3.0.0",
+--     "paths": {
+--         "/user": {
+--             "get": {
+--                 "responses": {
+--                     "200": {
+--                         "content": {
+--                             "application/json": {
+--                                 "schema": {
+--                                     "$ref": "#/components/schemas/Day"
+--                                 }
+--                             }
+--                         },
+--                         "description": ""
+--                     }
+--                 }
+--             }
+--         }
+--     }
+-- }
 --
 -- See also @'setResponseWith'@.
 setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
diff --git a/src/Data/OpenApi/Optics.hs b/src/Data/OpenApi/Optics.hs
--- a/src/Data/OpenApi/Optics.hs
+++ b/src/Data/OpenApi/Optics.hs
@@ -22,43 +22,88 @@
 -- Example from the "Data.OpenApi" module using @optics@:
 --
 -- >>> :{
--- BSL.putStrLn $ encode $ (mempty :: OpenApi)
+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)
 --   & #components % #schemas .~ [ ("User", mempty & #type ?~ OpenApiString) ]
 --   & #paths .~
 --     [ ("/user", mempty & #get ?~ (mempty
 --         & at 200 ?~ ("OK" & #_Inline % #content % at "application/json" ?~ (mempty & #schema ?~ Ref (Reference "User")))
 --         & at 404 ?~ "User info not found")) ]
 -- :}
--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"404":{"description":"User info not found"},"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"OK"}}}}},"components":{"schemas":{"User":{"type":"string"}}}}
+-- {
+--     "components": {
+--         "schemas": {
+--             "User": {
+--                 "type": "string"
+--             }
+--         }
+--     },
+--     "info": {
+--         "title": "",
+--         "version": ""
+--     },
+--     "openapi": "3.0.0",
+--     "paths": {
+--         "/user": {
+--             "get": {
+--                 "responses": {
+--                     "200": {
+--                         "content": {
+--                             "application/json": {
+--                                 "schema": {
+--                                     "$ref": "#/components/schemas/User"
+--                                 }
+--                             }
+--                         },
+--                         "description": "OK"
+--                     },
+--                     "404": {
+--                         "description": "User info not found"
+--                     }
+--                 }
+--             }
+--         }
+--     }
+-- }
 --
 -- For convenience optics are defined as /labels/. It means that field accessor
 -- names can be overloaded for different types. One such common field is
 -- @#description@. Many components of a Swagger specification can have
 -- descriptions, and you can use the same name for them:
 --
--- >>> BSL.putStrLn $ encode $ (mempty :: Response) & #description .~ "No content"
--- {"description":"No content"}
+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & #description .~ "No content"
+-- {
+--     "description": "No content"
+-- }
 -- >>> :{
--- BSL.putStrLn $ encode $ (mempty :: Schema)
+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema)
 --   & #type        ?~ OpenApiBoolean
 --   & #description ?~ "To be or not to be"
 -- :}
--- {"type":"boolean","description":"To be or not to be"}
+-- {
+--     "description": "To be or not to be",
+--     "type": "boolean"
+-- }
 --
 -- Additionally, to simplify working with @'Response'@, both @'Operation'@ and
 -- @'Responses'@ have direct access to it via @'Optics.Core.At.at'@. Example:
 --
 -- >>> :{
--- BSL.putStrLn $ encode $ (mempty :: Operation)
+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation)
 --   & at 404 ?~ "Not found"
 -- :}
--- {"responses":{"404":{"description":"Not found"}}}
---
+-- {
+--     "responses": {
+--         "404": {
+--             "description": "Not found"
+--         }
+--     }
+-- }
 module Data.OpenApi.Optics () where
 
 import Data.Aeson (Value)
 import Data.Scientific (Scientific)
 import Data.OpenApi.Internal
+import Data.OpenApi.Internal.Utils
 import Data.Text (Text)
 import Optics.Core
 import Optics.TH
diff --git a/src/Data/OpenApi/Schema.hs b/src/Data/OpenApi/Schema.hs
--- a/src/Data/OpenApi/Schema.hs
+++ b/src/Data/OpenApi/Schema.hs
@@ -13,8 +13,6 @@
   toSchemaRef,
   schemaName,
   toInlinedSchema,
-  ToSchema1(..),
-  BySchema1(..),
 
   -- * Generic schema encoding
   genericDeclareNamedSchema,
