diff --git a/Data/ByteString/FastBuilder.hs b/Data/ByteString/FastBuilder.hs
--- a/Data/ByteString/FastBuilder.hs
+++ b/Data/ByteString/FastBuilder.hs
@@ -1,14 +1,68 @@
 -- | An efficient implementation of ByteString builder.
 --
 -- In many cases, this module works as a drop-in replacement for
--- Data.ByteString.Builder, and should improve speed. However, one caveat
--- applies: when using 'toLazyByteString', if you consume the result
+-- Data.ByteString.Builder, and should improve speed.
+--
+-- = Performance tips
+--
+-- fast-builder should be faster than the standard builder in most situations.
+-- However, by following certain code patterns, you can often achive even
+-- more efficient code, with almost no memory allocation aside from the
+-- resulting 'ByteString' itself. The below are a list of hints for writing
+-- efficient code for constructing builders.
+--
+-- == Return builders directly from your function
+--
+-- Once you construct a builder, it's usually a good idea to just return it from
+-- your function. Avoid storing it in a data structure or passing it to another
+-- function, unless they are going to be eliminated by the compiler.
+-- Schematically, prefer this:
+--
+-- @
+-- good :: YourDataStructure -> Builder
+-- good d = serializeThis (this d) <> serializeThat (that d)
+-- @
+--
+-- over:
+--
+-- @
+-- bad0 :: YourDataStructure -> (Int, Builder)
+-- bad0 d
+--    = (compute d, serializeThis (this d) <> serializeThat (that d))
+-- @
+--
+-- or:
+--
+-- @
+-- bad1 :: YourDataStructure -> Builder
+-- bad1 d = serializeMore d (serializeThis (this d))
+-- @
+--
+-- An important special case of this general rule is to prefer foldr over
+-- foldl' when serializing a list, and to prefer structural recursion over
+-- tail recursion in general.
+--
+-- === Use 'rebuild'
+--
+-- When your function returns a different builder depending on the input,
+-- it's usually a good idea to use 'rebuild' to wrap the whole body of your
+-- function.  See the documentation for 'rebuild' for details.
+--
+-- === Background
+--
+-- Why is it good to return builders directly? It is because they are
+-- implemented as functions. When storing a function in a data structure or
+-- passing it around, you need to first allocate a closure for it. However,
+-- if you are just returning it, the returned function can be merged with your
+-- function, creating a function with a larger arity. For example, GHC can
+-- compile the @good@ function above into a 5-ary function, which requires
+-- no runtime allocation (the exact arity depends on the library version).
+--
+-- == Watch out for lazy ByteString generation
+--
+-- When using 'toLazyByteString', if you consume the result
 -- in a bound thread, performance degrades significantly. See the
 -- documentation for 'toLazyByteString' for details.
---
--- Sometimes performance can be greatly improved by inserting calls to
--- 'rebuild' to your program. See the documentation for 'rebuild' for
--- details.
 module Data.ByteString.FastBuilder
   (
   -- * The type
@@ -20,6 +74,7 @@
   , toLazyByteStringWith
   , toStrictByteString
   , hPutBuilder
+  , hPutBuilderWith
 
   -- * Performance tuning
   , rebuild
diff --git a/Data/ByteString/FastBuilder/Internal.hs b/Data/ByteString/FastBuilder/Internal.hs
--- a/Data/ByteString/FastBuilder/Internal.hs
+++ b/Data/ByteString/FastBuilder/Internal.hs
@@ -39,6 +39,7 @@
   , toLazyByteStringWith
   , toStrictByteString
   , hPutBuilder
+  , hPutBuilderWith
 
   -- * Basic builders
   , primBounded
@@ -51,6 +52,7 @@
   , byteStringCopyNoCheck
   , byteStringInsert
   , unsafeCString
+  , unsafeCStringLen
   , ensureBytes
   , getBytes
 
@@ -130,7 +132,7 @@
     -- ^ The destination of data changes while the builder is running.
   | GrowingBuffer !(IORef (ForeignPtr Word8))
     -- ^ Bytes are accumulated in a contiguous buffer.
-  | HandleSink !IO.Handle !(IORef Queue)
+  | HandleSink !IO.Handle !Int{-next buffer size-} !(IORef Queue)
     -- ^ Bytes are first accumulated in the 'Queue', then flushed to the
     -- 'IO.Handle'.
 
@@ -467,12 +469,18 @@
 
 -- | Output a 'Builder' to a 'IO.Handle'.
 hPutBuilder :: IO.Handle -> Builder -> IO ()
-hPutBuilder !h builder = do
-  let cap = 100
-  fptr <- mallocForeignPtrBytes cap
+hPutBuilder !h builder = hPutBuilderWith h 100 4096 builder
+
+-- | Like 'hPutBuffer', but allows the user to specify the initial
+-- and the subsequent desired buffer sizes. This function may be useful for
+-- setting large buffer when high throughput I/O is needed.
+hPutBuilderWith :: IO.Handle -> Int -> Int -> Builder -> IO ()
+hPutBuilderWith !h !initialCap !nextCap builder = do
+  fptr <- mallocForeignPtrBytes initialCap
   qRef <- newIORef $ Queue fptr 0
   let !base = unsafeForeignPtrToPtr fptr
-  cur <- runBuilder builder (HandleSink h qRef) base (base `plusPtr` cap)
+  cur <- runBuilder builder (HandleSink h nextCap qRef)
+    base (base `plusPtr` initialCap)
   flushQueue h qRef cur
 
 ----------------------------------------------------------------
@@ -591,7 +599,7 @@
       when (r < S.length bstr) $
         growBuffer bufRef (S.length bstr)
       useBuilder $ byteStringCopyNoCheck bstr
-    HandleSink h queueRef -> do
+    HandleSink h _nextCap queueRef -> do
       cur <- getCur
       io $ flushQueue h queueRef cur
       io $ S.hPut h bstr
@@ -602,14 +610,18 @@
 unsafeCString :: CString -> Builder
 unsafeCString cstr = rebuild $ let
     !len = fromIntegral $ c_pure_strlen cstr
-  in
-  mappend (ensureBytes len) $ mkBuilder $ do
-  cur <- getCur
-  io $ copyBytes cur (castPtr cstr) len
-  setCur $ cur `plusPtr` len
+  in unsafeCStringLen (cstr, len)
 
 foreign import ccall unsafe "strlen" c_pure_strlen :: CString -> CSize
 
+-- | Turn a 'CStringLen' into a 'Builder'. The behavior is undefined if the
+-- given 'CStringLen' does not point to a constant memory block.
+unsafeCStringLen :: CStringLen -> Builder
+unsafeCStringLen (ptr, len) = mappend (ensureBytes len) $ mkBuilder $ do
+  cur <- getCur
+  io $ copyBytes cur (castPtr ptr) len
+  setCur $ cur `plusPtr` len
+
 -- | @'ensureBytes' n@ ensures that at least @n@ bytes of free space is
 -- available in the current buffer, by allocating a new buffer when
 -- necessary.
@@ -638,10 +650,10 @@
         BoundedGrowingBuffer fptr bound ->
           growBufferBounded dRef fptr bound (I# n)
     GrowingBuffer bufRef -> growBuffer bufRef (I# n)
-    HandleSink h queueRef -> do
+    HandleSink h nextCap queueRef -> do
       cur <- getCur
       io $ flushQueue h queueRef cur
-      switchQueue queueRef $ max 4096 (I# n)
+      switchQueue queueRef $ max nextCap (I# n)
 {-# NOINLINE getBytes_ #-}
 
 -- | Return the remaining size of the current buffer, in bytes.
diff --git a/Data/ByteString/FastBuilder/Unsafe.hs b/Data/ByteString/FastBuilder/Unsafe.hs
new file mode 100644
--- /dev/null
+++ b/Data/ByteString/FastBuilder/Unsafe.hs
@@ -0,0 +1,7 @@
+-- | Unsafe functions.
+module Data.ByteString.FastBuilder.Unsafe
+  ( unsafeCString
+  , unsafeCStringLen
+  ) where
+
+import Data.ByteString.FastBuilder.Internal
diff --git a/fast-builder.cabal b/fast-builder.cabal
--- a/fast-builder.cabal
+++ b/fast-builder.cabal
@@ -1,5 +1,5 @@
 name:                fast-builder
-version:             0.0.0.6
+version:             0.0.1.0
 synopsis:            Fast ByteString Builder
 description:         An efficient implementation of ByteString builder. It should be faster than
                      the standard implementation in most cases.
@@ -20,11 +20,12 @@
 
 library
   exposed-modules:     Data.ByteString.FastBuilder,
-                       Data.ByteString.FastBuilder.Internal
+                       Data.ByteString.FastBuilder.Internal,
+                       Data.ByteString.FastBuilder.Unsafe
   other-modules:       Data.ByteString.FastBuilder.Internal.Prim
 
   -- other-extensions:
-  build-depends:       base >= 4.8 && < 4.10, bytestring >= 0.10.6.0, ghc-prim
+  build-depends:       base >= 4.8 && < 4.11, bytestring >= 0.10.6.0, ghc-prim
   -- hs-source-dirs:
   default-language:    Haskell2010
   ghc-options:         -Wall -g
@@ -40,6 +41,7 @@
     template-haskell, true-name >= 0.1.0.0
   ghc-options:         -fsimpl-tick-factor=120
   ghc-options:         -Wall -threaded -g -rtsopts
+  default-language:    Haskell2010
 
 benchmark vector
   type:                exitcode-stdio-1.0
@@ -47,6 +49,7 @@
   hs-source-dirs:      benchmarks
   build-depends:       base, fast-builder, criterion, bytestring, vector, deepseq
   ghc-options:         -Wall -threaded -g -rtsopts -eventlog
+  default-language:    Haskell2010
 
 benchmark map
   type:                exitcode-stdio-1.0
@@ -54,6 +57,7 @@
   hs-source-dirs:      benchmarks
   build-depends:       base, fast-builder, criterion, bytestring, containers, deepseq
   ghc-options:         -Wall -threaded -g -rtsopts -eventlog
+  default-language:    Haskell2010
 
 test-suite prop
   type:                exitcode-stdio-1.0
@@ -61,6 +65,7 @@
   hs-source-dirs:      tests
   build-depends:       base, fast-builder, bytestring, QuickCheck, stm, process
   ghc-options:         -Wall -g -rtsopts
+  default-language:    Haskell2010
 
 source-repository head
   type:                git
