diff --git a/avro.cabal b/avro.cabal
--- a/avro.cabal
+++ b/avro.cabal
@@ -2,14 +2,14 @@
 -- see http://haskell.org/cabal/users-guide/
 
 name:                avro
-version:             0.1.0.1
+version:             0.2.0.0
 synopsis:            Avro serialization support for Haskell
 description:         Avro serialization and deserialization support for Haskell
-homepage:            https://github.com/GaloisInc/avro.git
+homepage:            https://github.com/haskell-works/hw-haskell-avro.git
 license:             BSD3
 license-file:        LICENSE
 author:              Thomas M. DuBuisson
-maintainer:          tommd@galois.com
+maintainer:          Alexey Raga <alexey.raga@gmail.com>
 -- copyright:
 category:            Data
 build-type:          Simple
@@ -39,7 +39,10 @@
                         Data.Avro.Schema,
                         Data.Avro.Types,
                         Data.Avro.Zag,
-                        Data.Avro.Zig
+                        Data.Avro.Zig,
+                        Data.Avro.HasAvroSchema,
+                        Data.Avro.FromAvro,
+                        Data.Avro.ToAvro
   other-extensions:    OverloadedStrings
   build-depends:       base >=4.8 && <5.0,
                        aeson,
diff --git a/src/Data/Avro.hs b/src/Data/Avro.hs
--- a/src/Data/Avro.hs
+++ b/src/Data/Avro.hs
@@ -71,11 +71,13 @@
 module Data.Avro
   ( FromAvro(..)
   , ToAvro(..)
+  , HasAvroSchema(..)
   , Avro
   , (.:)
   , (.=), record
   , Result(..), badValue
   , decode
+  , decodeWithSchema
   , decodeContainer
   , decodeContainerBytes
   , encode
@@ -109,9 +111,21 @@
 import qualified Data.Vector          as V
 import           Data.Word
 
+import Data.Avro.FromAvro
+import Data.Avro.ToAvro
+import Data.Avro.HasAvroSchema
+
+type Avro a = (FromAvro a, ToAvro a)
+
 -- |Decode a lazy bytestring using a given Schema.
-decode :: FromAvro a => Schema -> ByteString -> Result a
-decode sch bytes =
+decode :: forall a. FromAvro a => ByteString -> Result a
+decode bytes =
+  case D.decodeAvro (untag (schema :: Tagged a Type)) bytes of
+      Right val -> fromAvro val
+      Left err  -> Error err
+
+decodeWithSchema :: FromAvro a => Schema -> ByteString -> Result a
+decodeWithSchema sch bytes =
   case D.decodeAvro sch bytes of
     Right val -> fromAvro val
     Left err  -> Error err
@@ -161,161 +175,9 @@
                                  G.bytesRead
        G.getLazyByteString (end-start)
 
-type Avro a = (FromAvro a, ToAvro a)
-class FromAvro a where
-  fromAvro :: Value Type -> Result a
-
-instance FromAvro (Value Type) where
-  fromAvro = pure
-instance (ToAvro a, ToAvro b, FromAvro a, FromAvro b) => FromAvro (Either a b) where
-  fromAvro e@(T.Union _ v x) =
-    if | v == untag (schema :: Tagged a Type) -> Left <$> fromAvro x
-       | v == untag (schema :: Tagged b Type) -> Right <$> fromAvro x
-       | otherwise -> badValue e "either"
-  fromAvro x = badValue x "either"
-instance FromAvro Bool where
-  fromAvro (T.Boolean b) = pure b
-  fromAvro v             = badValue v "Bool"
-instance FromAvro B.ByteString where
-  fromAvro (T.Bytes b) = pure b
-  fromAvro v          = badValue v "ByteString"
-instance FromAvro BL.ByteString where
-  fromAvro (T.Bytes b) = pure (BL.fromStrict b)
-  fromAvro v          = badValue v "Lazy ByteString"
-instance FromAvro Int where
-  fromAvro (T.Int i) | (fromIntegral i :: Integer) < fromIntegral (maxBound :: Int)
-                      = pure (fromIntegral i)
-  fromAvro (T.Long i) | (fromIntegral i :: Integer) < fromIntegral (maxBound :: Int)
-                      = pure (fromIntegral i)
-  fromAvro v          = badValue v "Int"
-instance FromAvro Int32 where
-  fromAvro (T.Int i)  = pure (fromIntegral i)
-  fromAvro v          = badValue v "Int32"
-instance FromAvro Int64 where
-  fromAvro (T.Long i) = pure i
-  fromAvro (T.Int i)  = pure (fromIntegral i)
-  fromAvro v = badValue v "Int64"
-instance FromAvro Double where
-  fromAvro (T.Double d) = pure d
-  fromAvro v            = badValue v "Double"
-instance FromAvro a => FromAvro (Maybe a) where
-  fromAvro (T.Union (S.Null :| [_])  _ T.Null) = pure Nothing
-  fromAvro (T.Union (S.Null :| [_]) _ v)       = Just <$> fromAvro v
-  fromAvro v = badValue v "Maybe a"
-
-instance FromAvro a => FromAvro [a] where
-  fromAvro (T.Array vec) = mapM fromAvro $ toList vec
-  fromAvro v = badValue v "[a]"
-
-instance FromAvro Text where
-  fromAvro (T.String txt) = pure txt
-  fromAvro v = badValue v "Text"
-
-instance FromAvro TL.Text where
-  fromAvro (T.String txt) = pure (TL.fromStrict txt)
-  fromAvro v = badValue v "Lazy Text"
-
-instance (FromAvro a) => FromAvro (Map.Map Text a) where
-  fromAvro (T.Record _ mp) = mapM fromAvro $ Map.fromList (HashMap.toList mp)
-  fromAvro (T.Map mp)  = mapM fromAvro $ Map.fromList (HashMap.toList mp)
-  fromAvro v = badValue v "Map Text a"
-
-instance (FromAvro a) => FromAvro (HashMap.HashMap Text a) where
-  fromAvro (T.Record _ mp) = mapM fromAvro mp
-  fromAvro (T.Map mp)    = mapM fromAvro mp
-  fromAvro v = badValue v "HashMap Text a"
-
-badValue :: Value Type -> String -> Result a
-badValue v t = fail $ "Unexpected value when decoding for '" <> t <> "': " <> show v
-
-(.:) :: FromAvro a => HashMap.HashMap Text (Value Type) -> Text -> Result a
-(.:) obj key =
-  case HashMap.lookup key obj of
-    Nothing -> fail $ "Requested field not available: " <> show key
-    Just v  -> fromAvro v
-
-(.=)  :: ToAvro a => Text -> a -> (Text,T.Value Type)
-(.=) nm val = (nm,toAvro val)
-
 record :: Foldable f => Type -> f (Text,T.Value Type) -> T.Value Type
 record ty = T.Record ty . HashMap.fromList . toList
 
-class ToAvro a where
-  toAvro :: a -> T.Value Type
-  schema :: Tagged a Type
-
-schemaOf :: (ToAvro a) => a -> Type
-schemaOf = witness schema
-
-instance ToAvro Bool where
-  toAvro = T.Boolean
-  schema = Tagged S.Boolean
-instance ToAvro () where
-  toAvro _ = T.Null
-  schema = Tagged S.Null
-instance ToAvro Int where
-  toAvro = T.Long . fromIntegral
-  schema = Tagged S.Long
-instance ToAvro Int32 where
-  toAvro = T.Int
-  schema = Tagged S.Int
-instance ToAvro Int64 where
-  toAvro = T.Long
-  schema = Tagged S.Long
-instance ToAvro Double where
-  toAvro = T.Double
-  schema = Tagged S.Double
-instance ToAvro Text.Text where
-  toAvro = T.String
-  schema = Tagged S.String
-instance ToAvro TL.Text where
-  toAvro = T.String . TL.toStrict
-  schema = Tagged S.String
-instance ToAvro B.ByteString where
-  toAvro = T.Bytes
-  schema = Tagged S.Bytes
-instance ToAvro BL.ByteString where
-  toAvro = T.Bytes . BL.toStrict
-  schema = Tagged S.Bytes
-instance (ToAvro a, ToAvro b) => ToAvro (Either a b) where
-  toAvro e =
-    let sch@(l:|[r]) = options (schemaOf e)
-    in case e of
-         Left a  -> T.Union sch l (toAvro a)
-         Right b -> T.Union sch r (toAvro b)
-  schema = Tagged $ mkUnion (untag (schema :: Tagged a Type) :| [untag (schema :: Tagged b Type)])
-instance (ToAvro a) => ToAvro (Map.Map Text a) where
-  toAvro = toAvro . HashMap.fromList . Map.toList
-  schema = wrapTag S.Map (schema :: Tagged a Type)
-instance (ToAvro a) => ToAvro (HashMap.HashMap Text a) where
-  toAvro = T.Map . HashMap.map toAvro
-  schema = wrapTag S.Map (schema :: Tagged a Type)
-instance (ToAvro a) => ToAvro (Map.Map TL.Text a) where
-  toAvro = toAvro . HashMap.fromList . map (first TL.toStrict) . Map.toList
-  schema = wrapTag S.Map (schema :: Tagged a Type)
-instance (ToAvro a) => ToAvro (HashMap.HashMap TL.Text a) where
-  toAvro = toAvro . HashMap.fromList . map (first TL.toStrict) . HashMap.toList
-  schema = wrapTag S.Map (schema :: Tagged a Type)
-instance (ToAvro a) => ToAvro (Map.Map String a) where
-  toAvro = toAvro . HashMap.fromList . map (first Text.pack) . Map.toList
-  schema = wrapTag S.Map (schema :: Tagged a Type)
-instance (ToAvro a) => ToAvro (HashMap.HashMap String a) where
-  toAvro = toAvro . HashMap.fromList . map (first Text.pack) . HashMap.toList
-  schema = wrapTag S.Map (schema :: Tagged a Type)
-instance (ToAvro a) => ToAvro (Maybe a) where
-  toAvro a =
-    let sch@(l:|[r]) = options (schemaOf a)
-    in case a of
-      Nothing -> T.Union sch S.Null (toAvro ())
-      Just v  -> T.Union sch r (toAvro v)
-  schema = Tagged $ mkUnion (S.Null:| [untag (schema :: Tagged a Type)])
-instance (ToAvro a) => ToAvro [a] where
-  toAvro = T.Array . V.fromList . (toAvro <$>)
-  schema = wrapTag S.Array (schema :: Tagged a Type)
-
-wrapTag :: (Type -> Type) -> Tagged a Type -> Tagged b Type
-wrapTag f = Tagged . f . untag
-{-# INLINE wrapTag #-}
 
 -- @enumToAvro val@ will generate an Avro encoded value of enum suitable
 -- for serialization ('encode').
diff --git a/src/Data/Avro/Deriving.hs b/src/Data/Avro/Deriving.hs
--- a/src/Data/Avro/Deriving.hs
+++ b/src/Data/Avro/Deriving.hs
@@ -40,19 +40,21 @@
 deriveAvro' :: Schema -> Q [Dec]
 deriveAvro' s = do
   let schemas = extractDerivables s
-  types <- traverse genType schemas
+  types     <- traverse genType schemas
+  hasSchema <- traverse genHasAvroSchema schemas
   fromAvros <- traverse genFromAvro schemas
-  toAvros <- traverse genToAvro schemas
-  pure $ join types <> join fromAvros <> join toAvros
+  toAvros   <- traverse genToAvro schemas
+  pure $ join types <> join hasSchema <> join fromAvros <> join toAvros
 
 -- | Derives "read only" Avro from a given schema file.
 -- Generates data types and FromAvro.
 deriveFromAvro :: FilePath -> Q [Dec]
 deriveFromAvro p = do
-  schemas <- extractDerivables <$> readSchema p
-  types <- traverse genType schemas
+  schemas   <- extractDerivables <$> readSchema p
+  types     <- traverse genType schemas
+  hasSchema <- traverse genHasAvroSchema schemas
   fromAvros <- traverse genFromAvro schemas
-  pure $ join types <> join fromAvros
+  pure $ join types <> join hasSchema <> join fromAvros
 
 readSchema :: FilePath -> Q Schema
 readSchema p = do
@@ -84,17 +86,25 @@
      )
   |]
 
-genToAvro :: Schema -> Q [Dec]
-genToAvro s@(Enum n _ _ _ vs _) = do
-  let sname = mkSchemaValueName n
+genHasAvroSchema :: Schema -> Q [Dec]
+genHasAvroSchema s = do
+  let sname = mkSchemaValueName (name s)
   sdef <- schemaDef sname s
-  idef <- toAvroInstance sname
+  idef <- hasAvroSchema sname
   pure (sdef <> idef)
   where
+    hasAvroSchema sname =
+      [d| instance HasAvroSchema $(conT $ mkDataTypeName (name s)) where
+            schema = pure $(varE sname)
+      |]
+
+genToAvro :: Schema -> Q [Dec]
+genToAvro s@(Enum n _ _ _ vs _) = do
+  toAvroInstance (mkSchemaValueName n)
+  where
     conP' = flip conP [] . mkAdtCtorName n
     toAvroInstance sname =
       [d| instance ToAvro $(conT $ mkDataTypeName n) where
-            schema = pure $(varE sname)
             toAvro = $([| \x ->
               let convert = AT.Enum $(varE sname) (fromEnum $([|x|]))
               in $(caseE [|x|] ((\v -> match (conP' v)
@@ -102,16 +112,12 @@
               |])
       |]
 
-genToAvro s@(Record n _ _ _ _ fs) = do
-  let sname = mkSchemaValueName n
-  sdef <- schemaDef sname s
-  idef <- toAvroInstance sname
-  pure (sdef <> idef)
+genToAvro s@(Record n _ _ _ _ fs) =
+  toAvroInstance (mkSchemaValueName n)
   where
     toAvroInstance sname =
       [d| instance ToAvro $(conT $ mkDataTypeName n) where
             toAvro = $(genToAvroFieldsExp sname)
-            schema = pure $(varE sname)
       |]
     genToAvroFieldsExp sname = [| \r -> record $(varE sname)
         $(let assign fld = [| T.pack $(mkTextLit (fldName fld)) .= $(varE $ mkFieldTextName n fld) r |]
diff --git a/src/Data/Avro/FromAvro.hs b/src/Data/Avro/FromAvro.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Avro/FromAvro.hs
@@ -0,0 +1,101 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE MultiWayIf #-}
+module Data.Avro.FromAvro
+
+where
+
+import           Control.Arrow        (first)
+import           Data.Avro.HasAvroSchema
+import qualified Data.Avro.Encode     as E
+import           Data.Avro.Schema     as S
+import           Data.Avro.Types      as T
+import qualified Data.ByteString      as B
+import           Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as BL
+import           Data.Foldable        (toList)
+import qualified Data.HashMap.Strict  as HashMap
+import           Data.Int
+import           Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.Map             as Map
+import           Data.Monoid          ((<>))
+import           Data.Text            (Text)
+import qualified Data.Text            as Text
+import qualified Data.Text.Lazy       as TL
+import           Data.Tagged
+import qualified Data.Vector          as V
+import           Data.Word
+
+class HasAvroSchema a => FromAvro a where
+  fromAvro :: Value Type -> Result a
+
+-- instance FromAvro (Value Type) where
+--   fromAvro = pure
+
+(.:) :: FromAvro a => HashMap.HashMap Text (Value Type) -> Text -> Result a
+(.:) obj key =
+  case HashMap.lookup key obj of
+    Nothing -> fail $ "Requested field not available: " <> show key
+    Just v  -> fromAvro v
+
+instance (FromAvro a, FromAvro b) => FromAvro (Either a b) where
+  fromAvro e@(T.Union _ v x) =
+    if | v == untag (schema :: Tagged a Type) -> Left  <$> fromAvro x
+       | v == untag (schema :: Tagged b Type) -> Right <$> fromAvro x
+       | otherwise -> badValue e "either"
+  fromAvro x = badValue x "either"
+instance FromAvro Bool where
+  fromAvro (T.Boolean b) = pure b
+  fromAvro v             = badValue v "Bool"
+instance FromAvro B.ByteString where
+  fromAvro (T.Bytes b) = pure b
+  fromAvro v          = badValue v "ByteString"
+instance FromAvro BL.ByteString where
+  fromAvro (T.Bytes b) = pure (BL.fromStrict b)
+  fromAvro v          = badValue v "Lazy ByteString"
+instance FromAvro Int where
+  fromAvro (T.Int i) | (fromIntegral i :: Integer) < fromIntegral (maxBound :: Int)
+                      = pure (fromIntegral i)
+  fromAvro (T.Long i) | (fromIntegral i :: Integer) < fromIntegral (maxBound :: Int)
+                      = pure (fromIntegral i)
+  fromAvro v          = badValue v "Int"
+instance FromAvro Int32 where
+  fromAvro (T.Int i)  = pure (fromIntegral i)
+  fromAvro v          = badValue v "Int32"
+instance FromAvro Int64 where
+  fromAvro (T.Long i) = pure i
+  fromAvro (T.Int i)  = pure (fromIntegral i)
+  fromAvro v = badValue v "Int64"
+instance FromAvro Double where
+  fromAvro (T.Double d) = pure d
+  fromAvro v            = badValue v "Double"
+instance FromAvro a => FromAvro (Maybe a) where
+  fromAvro (T.Union (S.Null :| [_])  _ T.Null) = pure Nothing
+  fromAvro (T.Union (S.Null :| [_]) _ v)       = Just <$> fromAvro v
+  fromAvro v = badValue v "Maybe a"
+
+instance FromAvro a => FromAvro [a] where
+  fromAvro (T.Array vec) = mapM fromAvro $ toList vec
+  fromAvro v = badValue v "[a]"
+
+instance FromAvro Text where
+  fromAvro (T.String txt) = pure txt
+  fromAvro v = badValue v "Text"
+
+instance FromAvro TL.Text where
+  fromAvro (T.String txt) = pure (TL.fromStrict txt)
+  fromAvro v = badValue v "Lazy Text"
+
+instance (FromAvro a) => FromAvro (Map.Map Text a) where
+  fromAvro (T.Record _ mp) = mapM fromAvro $ Map.fromList (HashMap.toList mp)
+  fromAvro (T.Map mp)  = mapM fromAvro $ Map.fromList (HashMap.toList mp)
+  fromAvro v = badValue v "Map Text a"
+
+instance (FromAvro a) => FromAvro (HashMap.HashMap Text a) where
+  fromAvro (T.Record _ mp) = mapM fromAvro mp
+  fromAvro (T.Map mp)    = mapM fromAvro mp
+  fromAvro v = badValue v "HashMap Text a"
+
+badValue :: Value Type -> String -> Result a
+badValue v t = fail $ "Unexpected value when decoding for '" <> t <> "': " <> show v
diff --git a/src/Data/Avro/HasAvroSchema.hs b/src/Data/Avro/HasAvroSchema.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Avro/HasAvroSchema.hs
@@ -0,0 +1,89 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Data.Avro.HasAvroSchema where
+
+import           Data.Avro.Schema     as S
+import           Data.Avro.Types      as T
+import qualified Data.ByteString      as B
+import           Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.HashMap.Strict  as HashMap
+import           Data.Int
+import qualified Data.Map             as Map
+import           Data.Monoid          ((<>))
+import           Data.Text            (Text)
+import qualified Data.Text            as Text
+import           Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.Text.Lazy       as TL
+import           Data.Tagged
+import qualified Data.Vector          as V
+import           Data.Word
+import Data.Proxy
+
+class HasAvroSchema a where
+  schema :: Tagged a Type
+
+schemaOf :: (HasAvroSchema a) => a -> Type
+schemaOf = witness schema
+
+instance HasAvroSchema Bool where
+  schema = Tagged S.Boolean
+
+instance HasAvroSchema () where
+  schema = Tagged S.Null
+
+instance HasAvroSchema Int where
+  schema = Tagged S.Long
+
+instance HasAvroSchema Int32 where
+  schema = Tagged S.Int
+
+instance HasAvroSchema Int64 where
+  schema = Tagged S.Long
+
+instance HasAvroSchema Double where
+  schema = Tagged S.Double
+
+instance HasAvroSchema Text.Text where
+  schema = Tagged S.String
+
+instance HasAvroSchema TL.Text where
+  schema = Tagged S.String
+
+instance HasAvroSchema B.ByteString where
+  schema = Tagged S.Bytes
+
+instance HasAvroSchema BL.ByteString where
+  schema = Tagged S.Bytes
+
+instance (HasAvroSchema a, HasAvroSchema b) => HasAvroSchema (Either a b) where
+  schema = Tagged $ mkUnion (untag (schema :: Tagged a Type) :| [untag (schema :: Tagged b Type)])
+
+instance (HasAvroSchema a) => HasAvroSchema (Map.Map Text a) where
+  schema = wrapTag S.Map (schema :: Tagged a Type)
+
+instance (HasAvroSchema a) => HasAvroSchema (HashMap.HashMap Text a) where
+  schema = wrapTag S.Map (schema :: Tagged a Type)
+
+instance (HasAvroSchema a) => HasAvroSchema (Map.Map TL.Text a) where
+  schema = wrapTag S.Map (schema :: Tagged a Type)
+
+instance (HasAvroSchema a) => HasAvroSchema (HashMap.HashMap TL.Text a) where
+  schema = wrapTag S.Map (schema :: Tagged a Type)
+
+instance (HasAvroSchema a) => HasAvroSchema (Map.Map String a) where
+  schema = wrapTag S.Map (schema :: Tagged a Type)
+
+instance (HasAvroSchema a) => HasAvroSchema (HashMap.HashMap String a) where
+  schema = wrapTag S.Map (schema :: Tagged a Type)
+
+instance (HasAvroSchema a) => HasAvroSchema (Maybe a) where
+  schema = Tagged $ mkUnion (S.Null:| [untag (schema :: Tagged a Type)])
+
+instance (HasAvroSchema a) => HasAvroSchema [a] where
+  schema = wrapTag S.Array (schema :: Tagged a Type)
+
+wrapTag :: (Type -> Type) -> Tagged a Type -> Tagged b Type
+wrapTag f = Tagged . f . untag
+{-# INLINE wrapTag #-}
diff --git a/src/Data/Avro/ToAvro.hs b/src/Data/Avro/ToAvro.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Avro/ToAvro.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Data.Avro.ToAvro
+
+where
+
+import           Control.Arrow        (first)
+import           Data.Avro.HasAvroSchema
+import           Data.Avro.Schema     as S
+import           Data.Avro.Types      as T
+import qualified Data.ByteString      as B
+import           Data.ByteString.Lazy (ByteString)
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.HashMap.Strict  as HashMap
+import           Data.Int
+import           Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.Map             as Map
+import           Data.Text            (Text)
+import qualified Data.Text            as Text
+import qualified Data.Text.Lazy       as TL
+import           Data.Tagged
+import qualified Data.Vector          as V
+import           Data.Word
+
+class HasAvroSchema a => ToAvro a where
+  toAvro :: a -> T.Value Type
+
+(.=)  :: ToAvro a => Text -> a -> (Text,T.Value Type)
+(.=) nm val = (nm,toAvro val)
+
+instance ToAvro Bool where
+  toAvro = T.Boolean
+
+instance ToAvro () where
+  toAvro _ = T.Null
+
+instance ToAvro Int where
+  toAvro = T.Long . fromIntegral
+
+instance ToAvro Int32 where
+  toAvro = T.Int
+
+instance ToAvro Int64 where
+  toAvro = T.Long
+
+instance ToAvro Double where
+  toAvro = T.Double
+
+instance ToAvro Text.Text where
+  toAvro = T.String
+
+instance ToAvro TL.Text where
+  toAvro = T.String . TL.toStrict
+
+instance ToAvro B.ByteString where
+  toAvro = T.Bytes
+
+instance ToAvro BL.ByteString where
+  toAvro = T.Bytes . BL.toStrict
+
+instance (ToAvro a, ToAvro b) => ToAvro (Either a b) where
+  toAvro e =
+    let sch@(l:|[r]) = options (schemaOf e)
+    in case e of
+         Left a  -> T.Union sch l (toAvro a)
+         Right b -> T.Union sch r (toAvro b)
+
+instance (ToAvro a) => ToAvro (Map.Map Text a) where
+  toAvro = toAvro . HashMap.fromList . Map.toList
+
+instance (ToAvro a) => ToAvro (HashMap.HashMap Text a) where
+  toAvro = T.Map . HashMap.map toAvro
+
+instance (ToAvro a) => ToAvro (Map.Map TL.Text a) where
+  toAvro = toAvro . HashMap.fromList . map (first TL.toStrict) . Map.toList
+
+instance (ToAvro a) => ToAvro (HashMap.HashMap TL.Text a) where
+  toAvro = toAvro . HashMap.fromList . map (first TL.toStrict) . HashMap.toList
+
+instance (ToAvro a) => ToAvro (Map.Map String a) where
+  toAvro = toAvro . HashMap.fromList . map (first Text.pack) . Map.toList
+
+instance (ToAvro a) => ToAvro (HashMap.HashMap String a) where
+  toAvro = toAvro . HashMap.fromList . map (first Text.pack) . HashMap.toList
+
+instance (ToAvro a) => ToAvro (Maybe a) where
+  toAvro a =
+    let sch@(l:|[r]) = options (schemaOf a)
+    in case a of
+      Nothing -> T.Union sch S.Null (toAvro ())
+      Just v  -> T.Union sch r (toAvro v)
+
+instance (ToAvro a) => ToAvro [a] where
+  toAvro = T.Array . V.fromList . (toAvro <$>)
+
diff --git a/test/Avro/Codec/ArraySpec.hs b/test/Avro/Codec/ArraySpec.hs
--- a/test/Avro/Codec/ArraySpec.hs
+++ b/test/Avro/Codec/ArraySpec.hs
@@ -14,8 +14,8 @@
 
 spec :: Spec
 spec = describe "Avro.Codec.ArraySpec" $ do
-  it "list roundtip" $ Q.property $ \(xs :: [Int]) -> decode (schemaOf xs) (encode xs) == Success xs
+  it "list roundtip" $ Q.property $ \(xs :: [Int]) -> decode (encode xs) == Success xs
 
   it "map roundtrip" $ Q.property $ \(xs :: Map String Int) ->
     let xs' = M.mapKeys T.pack xs
-    in decode (schemaOf xs') (encode xs') == Success xs'
+    in decode (encode xs') == Success xs'
diff --git a/test/Avro/Codec/BoolSpec.hs b/test/Avro/Codec/BoolSpec.hs
--- a/test/Avro/Codec/BoolSpec.hs
+++ b/test/Avro/Codec/BoolSpec.hs
@@ -30,11 +30,13 @@
         [ fld "onlyBoolValue" Boolean Nothing
         ]
 
+instance HasAvroSchema OnlyBool where
+  schema = pure onlyBoolSchema
+
 instance ToAvro OnlyBool where
   toAvro sa = record onlyBoolSchema
     [ "onlyBoolValue" .= onlyBoolValue sa
     ]
-  schema = pure onlyBoolSchema
 
 instance FromAvro OnlyBool where
   fromAvro (AT.Record _ r) =
@@ -42,7 +44,6 @@
 
 spec :: Spec
 spec = describe "Avro.Codec.BoolSpec" $ do
-  let x = untag (schema :: Tagged OnlyBool Type)
   it "should encode True correctly" $ do
     let trueEncoding = BL.singleton 0x01
     encode (OnlyBool True) `shouldBe` trueEncoding
@@ -52,7 +53,7 @@
     encode (OnlyBool False) `shouldBe` falseEncoding
 
   it "should encode then decode True correctly" $ do
-    decode x (encode $ OnlyBool True) `shouldBe` Success (OnlyBool True)
+    decode (encode $ OnlyBool True) `shouldBe` Success (OnlyBool True)
 
   it "should encode then decode False correctly" $ do
-    decode x (encode $ OnlyBool False) `shouldBe` Success (OnlyBool False)
+    decode (encode $ OnlyBool False) `shouldBe` Success (OnlyBool False)
diff --git a/test/Avro/Codec/DoubleSpec.hs b/test/Avro/Codec/DoubleSpec.hs
--- a/test/Avro/Codec/DoubleSpec.hs
+++ b/test/Avro/Codec/DoubleSpec.hs
@@ -24,10 +24,12 @@
         [ fld "onlyDoubleValue" Double Nothing
         ]
 
+instance HasAvroSchema OnlyDouble where
+  schema = pure onlyDoubleSchema
+
 instance ToAvro OnlyDouble where
   toAvro sa = record onlyDoubleSchema
     [ "onlyDoubleValue" .= onlyDoubleValue sa ]
-  schema = pure onlyDoubleSchema
 
 instance FromAvro OnlyDouble where
   fromAvro (AT.Record _ r) =
@@ -52,5 +54,4 @@
 
   it "Can decode encoded Double values" $ do
     Q.property $ \(d :: Double) ->
-      let x = untag (schema :: Tagged OnlyDouble Type) in
-        decode x (encode (OnlyDouble d)) == Success (OnlyDouble d)
+        decode (encode (OnlyDouble d)) == Success (OnlyDouble d)
diff --git a/test/Avro/Codec/Int64Spec.hs b/test/Avro/Codec/Int64Spec.hs
--- a/test/Avro/Codec/Int64Spec.hs
+++ b/test/Avro/Codec/Int64Spec.hs
@@ -37,11 +37,13 @@
         [ fld "onlyInt64Value"    Long Nothing
         ]
 
+instance HasAvroSchema OnlyInt64 where
+  schema = pure onlyInt64Schema
+
 instance ToAvro OnlyInt64 where
   toAvro sa = record onlyInt64Schema
     [ "onlyInt64Value" .= onlyInt64Value sa
     ]
-  schema = pure onlyInt64Schema
 
 instance FromAvro OnlyInt64 where
   fromAvro (AT.Record _ r) =
@@ -67,24 +69,20 @@
     let value = OnlyInt64 90071992547409917
     encode value `shouldBe` expectedBuffer
   it "Can decode 90071992547409917L correctly" $ do
-    let x = untag (schema :: Tagged OnlyInt64 Type)
     let buffer = BL.pack [0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x02]
     let expectedValue = OnlyInt64 90071992547409917
-    decode x buffer `shouldBe` Success expectedValue
+    decode buffer `shouldBe` Success expectedValue
   it "Can decode encoded Int64 values" $ do
-    let x = untag (schema :: Tagged OnlyInt64 Type)
-    Q.property $ \(w :: Int64) -> decode x (encode (OnlyInt64 w)) == Success (OnlyInt64 w)
+    Q.property $ \(w :: Int64) -> decode (encode (OnlyInt64 w)) == Success (OnlyInt64 w)
 
   it "Can decode 129L" $ do
     let w = 129 :: Int64
-    let x = untag (schema :: Tagged OnlyInt64 Type)
-    decode x (encode (OnlyInt64 w)) == Success (OnlyInt64 w)
+    decode (encode (OnlyInt64 w)) == Success (OnlyInt64 w)
 
   it "Can decode 36028797018963968 correctly" $ do
-    let x = untag (schema :: Tagged OnlyInt64 Type)
     let buffer = BL.pack (bitStringToWord8s "10000000 10000000 10000000 10000000 10000000 10000000 10000000 10000000 00000001")
     let expectedValue = OnlyInt64 36028797018963968
-    decode x buffer `shouldBe` Success expectedValue
+    decode buffer `shouldBe` Success expectedValue
 
   it "bitStringToWord8s 00000000"                   $  bitStringToWord8s "00000000"                    `shouldBe` [0x00             ]
   it "bitStringToWord8s 00000001"                   $  bitStringToWord8s "00000001"                    `shouldBe` [0x01             ]
diff --git a/test/Avro/Codec/MaybeSpec.hs b/test/Avro/Codec/MaybeSpec.hs
--- a/test/Avro/Codec/MaybeSpec.hs
+++ b/test/Avro/Codec/MaybeSpec.hs
@@ -27,11 +27,13 @@
         [ fld "onlyMaybeBoolValue" (mkUnion (Null :| [Boolean])) Nothing
         ]
 
+instance HasAvroSchema OnlyMaybeBool where
+  schema = pure onlyMaybeBoolSchema
+
 instance ToAvro OnlyMaybeBool where
   toAvro sa = record onlyMaybeBoolSchema
     [ "onlyMaybeBoolValue" .= onlyMaybeBoolValue sa
     ]
-  schema = pure onlyMaybeBoolSchema
 
 instance FromAvro OnlyMaybeBool where
   fromAvro (AT.Record _ r) =
@@ -41,5 +43,4 @@
 spec = describe "Avro.Codec.MaybeSpec" $ do
   it "should encode then decode Maybe Bool correctly" $ do
     Q.property $ \(w :: Maybe Bool) ->
-      let x = untag (schema :: Tagged OnlyMaybeBool Type) in
-        decode x (encode (OnlyMaybeBool w)) `shouldBe` Success (OnlyMaybeBool w)
+      decode (encode (OnlyMaybeBool w)) `shouldBe` Success (OnlyMaybeBool w)
diff --git a/test/Avro/Codec/NestedSpec.hs b/test/Avro/Codec/NestedSpec.hs
--- a/test/Avro/Codec/NestedSpec.hs
+++ b/test/Avro/Codec/NestedSpec.hs
@@ -36,12 +36,17 @@
         [ fld "parentValue1" Long             Nothing
         , fld "parentValue2" (Array childTypeSchema)  Nothing]
 
+instance HasAvroSchema ParentType where
+  schema = pure parentTypeSchema
+
+instance HasAvroSchema ChildType where
+  schema = pure childTypeSchema
+
 instance ToAvro ChildType where
   toAvro sa = record childTypeSchema
     [ "childValue1" .= childValue1 sa
     , "childValue2" .= childValue2 sa
     ]
-  schema = pure childTypeSchema
 
 instance FromAvro ChildType where
   fromAvro (AT.Record _ r) =
@@ -54,7 +59,6 @@
     [ "parentValue1" .= parentValue1 sa
     , "parentValue2" .= parentValue2 sa
     ]
-  schema = pure parentTypeSchema
 
 instance FromAvro ParentType where
   fromAvro (AT.Record _ r) =
@@ -68,5 +72,5 @@
     let parent = ParentType 0 [ChildType 1 2, ChildType 3 4]
     let parentEncoded = encode parent
 
-    let parentDecoded = decode parentTypeSchema parentEncoded
+    let parentDecoded = decode parentEncoded
     parentDecoded `shouldBe` Success parent
diff --git a/test/Avro/Codec/TextSpec.hs b/test/Avro/Codec/TextSpec.hs
--- a/test/Avro/Codec/TextSpec.hs
+++ b/test/Avro/Codec/TextSpec.hs
@@ -24,10 +24,12 @@
         [ fld "onlyTextValue" String Nothing
         ]
 
+instance HasAvroSchema OnlyText where
+  schema = pure onlyTextSchema
+
 instance ToAvro OnlyText where
   toAvro sa = record onlyTextSchema
     [ "onlyTextValue" .= onlyTextValue sa ]
-  schema = pure onlyTextSchema
 
 instance FromAvro OnlyText where
   fromAvro (AT.Record _ r) =
@@ -43,5 +45,4 @@
 
   it "Can decode encoded Text values" $ do
     Q.property $ \(t :: String) ->
-      let x = untag (schema :: Tagged OnlyText Type) in
-        decode x (encode (OnlyText (pack t))) == Success (OnlyText (pack t))
+      decode (encode (OnlyText (pack t))) == Success (OnlyText (pack t))
diff --git a/test/Avro/THSimpleSpec.hs b/test/Avro/THSimpleSpec.hs
--- a/test/Avro/THSimpleSpec.hs
+++ b/test/Avro/THSimpleSpec.hs
@@ -37,6 +37,6 @@
   it "should do full round trip" $
     forM_ msgs $ \msg -> do
       let encoded = encode msg
-      let decoded = decode (schemaOf msg) encoded
+      let decoded = decode encoded
 
       pure msg `shouldBe` decoded
diff --git a/test/Avro/ToAvroSpec.hs b/test/Avro/ToAvroSpec.hs
--- a/test/Avro/ToAvroSpec.hs
+++ b/test/Avro/ToAvroSpec.hs
@@ -39,6 +39,9 @@
         , fld "attraction" Double Nothing
         ]
 
+instance HasAvroSchema TypesTestMessage where
+  schema = pure tmSchema
+
 instance ToAvro TypesTestMessage where
   toAvro m = record tmSchema
     [ "id"          .= tmId m
@@ -48,7 +51,6 @@
     , "competence"  .= tmCompetence m
     , "attraction"  .= tmAttraction m
     ]
-  schema = pure tmSchema
 
 instance FromAvro TypesTestMessage where
   fromAvro (AT.Record _ r) =
