diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+1.0.1.1
+-------
+- Add more documentation on queries.
+
 1.0.1
 ------
 - Address an issue whereby Cassandra 'Error' responses might be mistakenly
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/cql-io.cabal b/cql-io.cabal
--- a/cql-io.cabal
+++ b/cql-io.cabal
@@ -1,5 +1,5 @@
 name:                 cql-io
-version:              1.0.1
+version:              1.0.1.1
 synopsis:             Cassandra CQL client.
 license:              MPL-2.0
 license-file:         LICENSE
diff --git a/src/Database/CQL/IO.hs b/src/Database/CQL/IO.hs
--- a/src/Database/CQL/IO.hs
+++ b/src/Database/CQL/IO.hs
@@ -24,32 +24,6 @@
 -- > shutdown c
 -- @
 --
---
--- __Note on prepared statements__
---
--- Prepared statements are fully supported but imply certain
--- complexities which lead to some assumptions beyond the scope
--- of the CQL binary protocol specification (spec):
---
--- (1) The spec scopes the 'QueryId' to the node the query has
---     been prepared with. The spec does not state anything
---     about the format of the 'QueryId', however it seems that
---     at least the official Java driver assumes that any given
---     'QueryString' yields the same 'QueryId' on every node.
---     We make the same assumption.
--- (2) In case a node does not know a given 'QueryId' an 'Unprepared'
---     error is returned. We assume that it is always safe to then
---     transparently re-prepare the corresponding 'QueryString' and
---     to re-execute the original request against the same node.
---
--- Besides these assumptions there is also a potential tradeoff in
--- regards to /eager/ vs. /lazy/ query preparation.
--- We understand /eager/ to mean preparation against all current nodes of
--- a cluster and /lazy/ to mean preparation against a single node if
--- required, i.e. after an 'Unprepared' error response. Which strategy to
--- choose depends on the scope of query reuse and the size of the cluster.
--- The global default can be changed through the 'Settings' module and per
--- action using 'withPrepareStrategy'.
 
 {-# LANGUAGE DeriveFunctor #-}
 {-# LANGUAGE LambdaCase    #-}
@@ -127,14 +101,16 @@
     , debugInfo
 
       -- * Queries
+      -- $queries
     , R, W, S
     , QueryParams       (..)
     , defQueryParams
     , Consistency       (..)
     , SerialConsistency (..)
-    , QueryString       (..)
+    , Identity          (..)
 
       -- ** Basic Queries
+    , QueryString (..)
     , query
     , query1
     , write
@@ -169,10 +145,7 @@
     , once
 
       -- ** Low-Level Queries
-      --
-      -- | Note: Use of these low-level functions may require additional imports from
-      -- @Database.CQL.Protocol@ or its submodules in order to construct
-      -- 'Request's and evaluate 'Response's.
+      -- $low-level-queries
     , RunQ (..)
     , request
 
@@ -189,6 +162,7 @@
 
 import Control.Applicative
 import Control.Monad.Catch
+import Data.Functor.Identity
 import Data.Maybe (isJust, listToMaybe)
 import Database.CQL.Protocol
 import Database.CQL.IO.Batch hiding (batch)
@@ -202,6 +176,64 @@
 import Prelude hiding (init)
 
 import qualified Database.CQL.IO.Batch as B
+
+-- $queries
+--
+-- Queries are defined either as 'QueryString's or 'PrepQuery's.
+-- Both types carry three phantom type parameters used to describe
+-- the query, input and output types, respectively, as follows:
+--
+--   * @__k__@ is one of 'R'ead, 'W'rite or 'S'chema.
+--   * @__a__@ is the tuple type for the input, i.e. for the
+--     parameters bound by positional (@?@) or named (@:foo@) placeholders.
+--   * @__b__@ is the tuple type for the outputs, i.e. for the
+--     columns selected in a query.
+--
+-- Thereby every type used in an input or output tuple must be an instance
+-- of the 'Cql' typeclass. It is the responsibility of user code
+-- that the type ascription of a query matches the order, number and types of
+-- the parameters. For example:
+--
+-- @
+-- myQuery :: QueryString R (Identity UUID) (Text, Int, Maybe UTCTime)
+-- myQuery = "select name, age, birthday from user where id = ?"
+-- @
+--
+-- In this example, the query is declared as a 'R'ead with a single
+-- input (id) and three outputs (name, age and birthday).
+--
+-- Note that a single input or output type needs to be wrapped
+-- in the 'Identity' newtype, for which there is a `Cql` instance,
+-- in order to avoid overlapping instances.
+--
+-- It is a common strategy to use additional @newtype@s with derived
+-- @Cql@ instances for additional type safety, e.g.
+--
+-- @
+-- newtype UserId = UserId UUID deriving (Eq, Show, Cql)
+-- @
+--
+-- The input and output tuples can further be automatically
+-- converted from and to records via the 'Database.CQL.Protocol.Record'
+-- typeclass, whose instances can be generated via @TemplateHaskell@,
+-- if desired.
+--
+-- __Note on null values__
+--
+-- In principle, any column in Cassandra is /nullable/, i.e. may be
+-- be set to @null@ as a result of row operations. It is therefore
+-- important that any output type of a query that may be null
+-- is wrapped in the 'Maybe' type constructor.
+-- It is a common pitfall that a column is assumed to never contain
+-- null values, when in fact partial updates or deletions on a row,
+-- including via the use of TTLs, may result in null values and thus
+-- runtime errors when processing the responses.
+
+-- $low-level-queries
+--
+-- /Note/: Use of the these functions may require additional imports from
+-- @Database.CQL.Protocol@ or its submodules in order to construct
+-- 'Request's and evaluate 'Response's.
 
 -- | A type which can be run as a query.
 class RunQ q where
diff --git a/src/Database/CQL/IO/PrepQuery.hs b/src/Database/CQL/IO/PrepQuery.hs
--- a/src/Database/CQL/IO/PrepQuery.hs
+++ b/src/Database/CQL/IO/PrepQuery.hs
@@ -35,8 +35,47 @@
 -----------------------------------------------------------------------------
 -- Prepared Query
 
--- | Representation of a prepared query.
--- Actual preparation is handled transparently by the driver.
+-- | Representation of a prepared 'QueryString'. A prepared query is
+-- executed in two stages:
+--
+--   1. The query string is sent to a server without parameters for
+--      preparation. The server responds with a 'QueryId'.
+--   2. The prepared query is executed by sending the 'QueryId'
+--      and parameters to the server.
+--
+-- Thereby step 1 is only performed when the query has not yet been prepared
+-- with the host (coordinator) used for query execution. Thus, prepared
+-- queries enhance performance by avoiding the repeated sending and parsing
+-- of query strings.
+--
+-- Query preparation is handled transparently by the client.
+-- See 'Database.CQL.IO.setPrepareStrategy'.
+--
+-- __Note__
+--
+-- Prepared statements are fully supported but rely on some
+-- assumptions beyond the scope of the CQL binary protocol
+-- specification (spec):
+--
+-- (1) The spec scopes the 'QueryId' to the node the query has
+--     been prepared with. The spec does not state anything
+--     about the format of the 'QueryId'. However the official
+--     Java driver assumes that any given 'QueryString' yields
+--     the same 'QueryId' on every node. This client make the
+--     same assumption.
+-- (2) In case a node does not know a given 'QueryId' an 'Unprepared'
+--     error is returned. We assume that it is always safe to
+--     transparently re-prepare the corresponding 'QueryString' and
+--     to re-execute the original request against the same node.
+--
+-- Besides these assumptions there is also a potential tradeoff in
+-- regards to /eager/ vs. /lazy/ query preparation.
+-- We understand /eager/ to mean preparation against all current nodes of
+-- a cluster and /lazy/ to mean preparation against a single node on demand,
+-- i.e. upon receiving an 'Unprepared' error response. Which strategy to
+-- choose depends on the scope of query reuse and the size of the cluster.
+-- The global default can be changed through the 'Settings' module as well
+-- as locally using 'withPrepareStrategy'.
 data PrepQuery k a b = PrepQuery
     { pqStr :: !(QueryString k a b)
     , pqId  :: !PrepQueryId
diff --git a/src/Database/CQL/IO/Settings.hs b/src/Database/CQL/IO/Settings.hs
--- a/src/Database/CQL/IO/Settings.hs
+++ b/src/Database/CQL/IO/Settings.hs
@@ -25,6 +25,7 @@
 
 import qualified Data.HashMap.Strict as HashMap
 
+-- | Strategy for the execution of 'PrepQuery's.
 data PrepareStrategy
     = EagerPrepare -- ^ cluster-wide preparation
     | LazyPrepare  -- ^ on-demand per node preparation
