diff --git a/avro.cabal b/avro.cabal
--- a/avro.cabal
+++ b/avro.cabal
@@ -2,10 +2,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 86dacf38d4a96f044259ea81c7afc0a5aec901f34480f50e90d4c98b05834e43
+-- hash: d96fb3f751e7082ce82dad5a68e5c9c64c853adfcdba51ba55c807b50491045b
 
 name:           avro
-version:        0.3.0.5
+version:        0.3.1.0
 synopsis:       Avro serialization support for Haskell
 description:    Avro serialization and deserialization support for Haskell
 category:       Data
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
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP               #-}
+{-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE LambdaCase        #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RecordWildCards   #-}
@@ -6,7 +7,15 @@
 {-# LANGUAGE ViewPatterns      #-}
 
 module Data.Avro.Deriving
-( deriveAvro
+( -- * Deriving options
+  DeriveOptions(..), defaultDeriveOptions
+, mkPrefixedFieldName, mkAsIsFieldName
+
+  -- * Deriving Haskell types from Avro schema
+, deriveAvroWithOptions
+, deriveAvroWithOptions'
+, deriveFromAvroWithOptions
+, deriveAvro
 , deriveAvro'
 , deriveFromAvro
 )
@@ -27,6 +36,7 @@
 import           Data.Map                      (Map)
 import           Data.Maybe                    (fromMaybe)
 import           Data.Semigroup                ((<>))
+import           GHC.Generics                  (Generic)
 import           Language.Haskell.TH           as TH
 import           Language.Haskell.TH.Syntax
 
@@ -42,28 +52,95 @@
 
 -- | Derives Avro from a given schema file.
 -- Generates data types, FromAvro and ToAvro instances.
-deriveAvro :: FilePath -> Q [Dec]
-deriveAvro p = readSchema p >>= deriveAvro'
+data DeriveOptions = DeriveOptions
+  { -- | How to build field names for generated data types
+    doFieldNameBuilder :: TypeName -> Field -> Name
+  }
 
-deriveAvro' :: Schema -> Q [Dec]
-deriveAvro' s = do
+-- | Default deriving options
+--
+-- @
+-- defaultDeriveOptions = 'DeriveOptions'
+--   { doFieldNameBuilder = 'mkPrefixedFieldName'
+--   }
+-- @
+defaultDeriveOptions = DeriveOptions
+  { doFieldNameBuilder = mkPrefixedFieldName
+  }
+
+-- | Generates a field name that is prefixed with the type name.
+--
+-- For example, if the schema defines type 'Person' that has a field 'firstName',
+-- then the generated Haskell type will be like
+--
+-- @
+-- Person { personFirstName :: Text }
+-- @
+mkPrefixedFieldName :: TypeName -> Field -> Name
+mkPrefixedFieldName (TN dn) fld = mkTextName . sanitiseName $
+  updateFirst T.toLower dn <> updateFirst T.toUpper (fldName fld)
+
+-- | Generates a field name that matches the field name in schema
+-- (sanitised for Haskell, so first letter is lower cased)
+--
+-- For example, if the schema defines type 'Person' that has a field 'firstName',
+-- then the generated Haskell type will be like
+--
+-- @
+-- Person { firstName :: Text }
+-- @
+-- You may want to enable 'DuplicateRecordFields' if you want to use this method.
+
+mkAsIsFieldName :: TypeName -> Field -> Name
+mkAsIsFieldName _ = mkTextName . sanitiseName . updateFirst T.toLower . fldName
+
+-- | Generates Haskell classes and 'FromAvro' and 'ToAvro' instances
+-- given the Avro schema file
+deriveAvroWithOptions :: DeriveOptions -> FilePath -> Q [Dec]
+deriveAvroWithOptions o p = readSchema p >>= deriveAvroWithOptions' o
+
+-- | Generates Haskell classes and 'FromAvro' and 'ToAvro' instances
+-- given the Avro schema
+deriveAvroWithOptions' :: DeriveOptions -> Schema -> Q [Dec]
+deriveAvroWithOptions' o s = do
   let schemas = extractDerivables s
-  types     <- traverse genType schemas
+  types     <- traverse (genType o) schemas
   hasSchema <- traverse genHasAvroSchema schemas
   fromAvros <- traverse genFromAvro schemas
-  toAvros   <- traverse genToAvro schemas
+  toAvros   <- traverse (genToAvro o) 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
+deriveFromAvroWithOptions :: DeriveOptions -> FilePath -> Q [Dec]
+deriveFromAvroWithOptions o p = do
   schemas   <- extractDerivables <$> readSchema p
-  types     <- traverse genType schemas
+  types     <- traverse (genType o) schemas
   hasSchema <- traverse genHasAvroSchema schemas
   fromAvros <- traverse genFromAvro schemas
   pure $ join types <> join hasSchema <> join fromAvros
 
+-- | Same as 'deriveAvroWithOptions' but uses 'defaultDeriveOptions'
+--
+-- @
+-- deriveAvro' = deriveAvroWithOptions' 'defaultDeriveOptions'
+-- @
+deriveAvro :: FilePath -> Q [Dec]
+deriveAvro = deriveAvroWithOptions defaultDeriveOptions
+
+-- | Same as 'deriveAvroWithOptions'' but uses 'defaultDeriveOptions'
+--
+-- @
+-- deriveAvro' = 'deriveAvroWithOptions'' 'defaultDeriveOptions'
+-- @
+deriveAvro' :: Schema -> Q [Dec]
+deriveAvro' = deriveAvroWithOptions' defaultDeriveOptions
+
+-- | Derives "read only" Avro from a given schema file.
+-- Generates data types and FromAvro.
+deriveFromAvro :: FilePath -> Q [Dec]
+deriveFromAvro = deriveFromAvroWithOptions defaultDeriveOptions
+
 readSchema :: FilePath -> Q Schema
 readSchema p = do
   qAddDependentFile p
@@ -111,8 +188,8 @@
             schema = pure $(varE sname)
       |]
 
-genToAvro :: Schema -> Q [Dec]
-genToAvro s@(Enum n _ _ _ vs _) =
+genToAvro :: DeriveOptions -> Schema -> Q [Dec]
+genToAvro opts s@(Enum n _ _ _ vs _) =
   toAvroInstance (mkSchemaValueName n)
   where
     conP' = flip conP [] . mkAdtCtorName n
@@ -125,7 +202,7 @@
               |])
       |]
 
-genToAvro s@(Record n _ _ _ _ fs) =
+genToAvro opts s@(Record n _ _ _ _ fs) =
   toAvroInstance (mkSchemaValueName n)
   where
     toAvroInstance sname =
@@ -133,12 +210,12 @@
             toAvro = $(genToAvroFieldsExp sname)
       |]
     genToAvroFieldsExp sname = [| \r -> record $(varE sname)
-        $(let assign fld = [| T.pack $(mkTextLit (fldName fld)) .= $(varE $ mkFieldTextName n fld) r |]
+        $(let assign fld = [| T.pack $(mkTextLit (fldName fld)) .= $(varE $ (doFieldNameBuilder opts) n fld) r |]
           in listE $ assign <$> fs
         )
       |]
 
-genToAvro s@(Fixed n _ _ size) =
+genToAvro opts s@(Fixed n _ _ size) =
   toAvroInstance (mkSchemaValueName n)
   where
     toAvroInstance sname =
@@ -246,19 +323,19 @@
     sn n (ValD (VarP _) x y) = ValD (VarP n) x y
     sn _ d                   = d
 
-genType :: Schema -> Q [Dec]
-genType (S.Record n _ _ _ _ fs) = do
-  flds <- traverse (mkField n) fs
+genType :: DeriveOptions -> Schema -> Q [Dec]
+genType opts (S.Record n _ _ _ _ fs) = do
+  flds <- traverse (mkField opts n) fs
   let dname = mkDataTypeName n
   sequenceA [genDataType dname flds]
-genType (S.Enum n _ _ _ vs _) = do
+genType _ (S.Enum n _ _ _ vs _) = do
   let dname = mkDataTypeName n
   sequenceA [genEnum dname (mkAdtCtorName n <$> vs)]
-genType (S.Fixed n _ _ s) = do
+genType _ (S.Fixed n _ _ s) = do
   let dname = mkDataTypeName n
   sequenceA [genNewtype dname]
 
-genType _ = pure []
+genType _ _ = pure []
 
 mkFieldTypeName :: S.Type -> Q TH.Type
 mkFieldTypeName t = case t of
@@ -311,32 +388,28 @@
 mkDataTypeName' =
   mkTextName . sanitiseName . updateFirst T.toUpper . T.takeWhileEnd (/='.')
 
-mkFieldTextName :: TypeName -> Field -> Name
-mkFieldTextName (TN dn) fld = mkTextName . sanitiseName $
-  updateFirst T.toLower dn <> updateFirst T.toUpper (fldName fld)
-
-mkField :: TypeName -> Field -> Q VarStrictType
-mkField prefix field = do
+mkField :: DeriveOptions -> TypeName -> Field -> Q VarStrictType
+mkField opts prefix field = do
   ftype <- mkFieldTypeName (fldType field)
-  let fName = mkFieldTextName prefix field
+  let fName = (doFieldNameBuilder opts) prefix field
   pure (fName, defaultStrictness, ftype)
 
 genNewtype :: Name -> Q Dec
 #if MIN_VERSION_template_haskell(2,12,0)
 genNewtype dn = do
-  ders <- sequenceA [[t|Eq|], [t|Show|]]
+  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
   fldType <- [t|ByteString|]
   let ctor = RecC dn [(mkName ("un" ++ nameBase dn), defaultStrictness, fldType)]
   pure $ NewtypeD [] dn [] Nothing ctor [DerivClause Nothing ders]
 #elif MIN_VERSION_template_haskell(2,11,0)
 genNewtype dn = do
-  ders <- sequenceA [[t|Eq|], [t|Show|]]
+  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
   fldType <- [t|ByteString|]
   let ctor = RecC dn [(mkName ("un" ++ nameBase dn), defaultStrictness, fldType)]
   pure $ NewtypeD [] dn [] Nothing ctor ders
 #else
 genNewtype dn = do
-  [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|]]
+  [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
   fldType <- [t|ByteString|]
   let ctor = RecC dn [(mkName ("un" ++ nameBase dn), defaultStrictness, fldType)]
   pure $ NewtypeD [] dn [] ctor [eq, sh]
@@ -345,30 +418,30 @@
 genEnum :: Name -> [Name] -> Q Dec
 #if MIN_VERSION_template_haskell(2,12,0)
 genEnum dn vs = do
-  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|]]
+  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|], [t|Generic|]]
   pure $ DataD [] dn [] Nothing ((\n -> NormalC n []) <$> vs) [DerivClause Nothing ders]
 #elif MIN_VERSION_template_haskell(2,11,0)
 genEnum dn vs = do
-  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|]]
+  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|], [t|Generic|]]
   pure $ DataD [] dn [] Nothing ((\n -> NormalC n []) <$> vs) ders
 #else
 genEnum dn vs = do
-  [ConT eq, ConT sh, ConT or, ConT en] <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|]]
+  [ConT eq, ConT sh, ConT or, ConT en] <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|], [t|Generic|]]
   pure $ DataD [] dn [] ((\n -> NormalC n []) <$> vs) [eq, sh, or, en]
 #endif
 
 genDataType :: Name -> [VarStrictType] -> Q Dec
 #if MIN_VERSION_template_haskell(2,12,0)
 genDataType dn flds = do
-  ders <- sequenceA [[t|Eq|], [t|Show|]]
+  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
   pure $ DataD [] dn [] Nothing [RecC dn flds] [DerivClause Nothing ders]
 #elif MIN_VERSION_template_haskell(2,11,0)
 genDataType dn flds = do
-  ders <- sequenceA [[t|Eq|], [t|Show|]]
+  ders <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
   pure $ DataD [] dn [] Nothing [RecC dn flds] ders
 #else
 genDataType dn flds = do
-  [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|]]
+  [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]
   pure $ DataD [] dn [] [RecC dn flds] [eq, sh]
 #endif
 
diff --git a/src/Data/Avro/Schema.hs b/src/Data/Avro/Schema.hs
--- a/src/Data/Avro/Schema.hs
+++ b/src/Data/Avro/Schema.hs
@@ -55,7 +55,8 @@
 import           Data.List.NonEmpty         (NonEmpty (..))
 import qualified Data.List.NonEmpty         as NE
 import           Data.Maybe                 (catMaybes, fromMaybe)
-import           Data.Monoid                (First (..), (<>))
+import           Data.Monoid                (First (..))
+import           Data.Semigroup
 import qualified Data.Set                   as S
 import           Data.String
 import           Data.Text                  (Text)
@@ -154,9 +155,12 @@
 instance Show TypeName where
   show (TN s) = show s
 
+instance Semigroup TypeName where
+  TN a <> TN b = TN (a <> b)
+
 instance Monoid TypeName where
   mempty = TN mempty
-  mappend (TN a) (TN b) = TN (a <> b)
+  mappend = (<>)
 
 instance IsString TypeName where
   fromString = TN . fromString
@@ -371,6 +375,8 @@
   mzero = fail "mzero"
   mplus a@(Success _) _ = a
   mplus _ b             = b
+instance Semigroup (Result a) where
+  (<>) = mplus
 instance Monoid (Result a) where
   mempty = fail "Empty Result"
   mappend = mplus
@@ -435,11 +441,11 @@
           Long   -> return $ Ty.Long   (floor i)
           Float  -> return $ Ty.Float  (realToFrac i)
           Double -> return $ Ty.Double (realToFrac i)
-          _                   -> avroTypeMismatch ty "number"
+          _      -> avroTypeMismatch ty "number"
       A.Array vec    ->
         case ty of
           Array t -> Ty.Array <$> V.mapM (parseAvroJSON union env t) vec
-          _  -> avroTypeMismatch ty "array"
+          _       -> avroTypeMismatch ty "array"
       A.Object obj ->
         case ty of
           Map mTy     -> Ty.Map <$> mapM (parseAvroJSON union env mTy) obj
@@ -454,7 +460,7 @@
           _ -> avroTypeMismatch ty "object"
       A.Null -> case ty of
                   Null -> return Ty.Null
-                  _ -> avroTypeMismatch ty "null"
+                  _    -> avroTypeMismatch ty "null"
 
 -- | Parses a string literal into a bytestring in the format expected
 -- for bytes and fixed values. Will fail if every character does not
diff --git a/test/Avro/Deconflict/Reader.hs b/test/Avro/Deconflict/Reader.hs
--- a/test/Avro/Deconflict/Reader.hs
+++ b/test/Avro/Deconflict/Reader.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell   #-}
 module Avro.Deconflict.Reader
diff --git a/test/Avro/Deconflict/Writer.hs b/test/Avro/Deconflict/Writer.hs
--- a/test/Avro/Deconflict/Writer.hs
+++ b/test/Avro/Deconflict/Writer.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TemplateHaskell   #-}
 module Avro.Deconflict.Writer
diff --git a/test/Avro/DefaultsSpec.hs b/test/Avro/DefaultsSpec.hs
--- a/test/Avro/DefaultsSpec.hs
+++ b/test/Avro/DefaultsSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
diff --git a/test/Avro/JSONSpec.hs b/test/Avro/JSONSpec.hs
--- a/test/Avro/JSONSpec.hs
+++ b/test/Avro/JSONSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
diff --git a/test/Avro/NormSchemaSpec.hs b/test/Avro/NormSchemaSpec.hs
--- a/test/Avro/NormSchemaSpec.hs
+++ b/test/Avro/NormSchemaSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
diff --git a/test/Avro/THEncodeContainerSpec.hs b/test/Avro/THEncodeContainerSpec.hs
--- a/test/Avro/THEncodeContainerSpec.hs
+++ b/test/Avro/THEncodeContainerSpec.hs
@@ -1,13 +1,14 @@
+{-# LANGUAGE DeriveGeneric   #-}
 {-# LANGUAGE TemplateHaskell #-}
 module Avro.THEncodeContainerSpec where
 
-import Data.Avro
-import Data.Avro.Deriving
+import           Data.Avro
+import           Data.Avro.Deriving
 
-import Test.Hspec
+import           Test.Hspec
 
-import Control.Monad (void)
-import Control.Exception
+import           Control.Exception
+import           Control.Monad      (void)
 
 deriveAvro "test/data/record.avsc"
 
diff --git a/test/Avro/THEnumSpec.hs b/test/Avro/THEnumSpec.hs
--- a/test/Avro/THEnumSpec.hs
+++ b/test/Avro/THEnumSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
@@ -7,7 +8,7 @@
 import           Data.Avro
 import           Data.Avro.Deriving
 
-import Test.Hspec
+import           Test.Hspec
 
 {-# ANN module ("HLint: ignore Redundant do"        :: String) #-}
 
diff --git a/test/Avro/THLogicalTypeSpec.hs b/test/Avro/THLogicalTypeSpec.hs
--- a/test/Avro/THLogicalTypeSpec.hs
+++ b/test/Avro/THLogicalTypeSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
diff --git a/test/Avro/THReusedSpec.hs b/test/Avro/THReusedSpec.hs
--- a/test/Avro/THReusedSpec.hs
+++ b/test/Avro/THReusedSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
@@ -7,7 +8,7 @@
 import           Data.Avro
 import           Data.Avro.Deriving
 
-import Test.Hspec
+import           Test.Hspec
 
 {-# ANN module ("HLint: ignore Redundant do"        :: String) #-}
 
diff --git a/test/Avro/THSimpleSpec.hs b/test/Avro/THSimpleSpec.hs
--- a/test/Avro/THSimpleSpec.hs
+++ b/test/Avro/THSimpleSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
diff --git a/test/Avro/THUnionSpec.hs b/test/Avro/THUnionSpec.hs
--- a/test/Avro/THUnionSpec.hs
+++ b/test/Avro/THUnionSpec.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE OverloadedStrings   #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TemplateHaskell     #-}
