diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,91 @@
+Scientific workflow management system
+=====================================
+
+A scientific workflow is a series of computational steps which usually can be presented as a Directed Acyclic Graph (DAG).
+
+SciFlow is to help programmers design complex workflows with ease. Here is a trivial example. (Since we use template haskell, we need to divide this small program into two parts.)
+
+```haskell
+---------------------------------------------------
+-- File 1: MyModule.hs
+---------------------------------------------------
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+module Functions
+    (builder) where
+
+import Control.Lens ((^.), (.=))
+import qualified Data.Text as T
+import Shelly hiding (FilePath)
+import Text.Printf (printf)
+
+import Scientific.Workflow
+
+create :: () -> IO FilePath
+create _ = do
+    writeFile "hello.txt" "hello world"
+    return "hello.txt"
+
+countWords :: FilePath -> IO Int
+countWords fl = do
+    content <- readFile fl
+    return $ length $ words content
+
+countChars :: FilePath -> IO Int
+countChars fl = do
+    content <- readFile fl
+    return $ sum $ map length $ words content
+
+output :: (Int, Int) -> IO Bool
+output (ws, cs) = do
+    putStrLn $ printf "Number of words: %d" ws
+    putStrLn $ printf "Number of characters: %d" cs
+    return True
+
+cleanUp :: (Bool, FilePath) -> IO ()
+cleanUp (toBeRemoved, fl) = if toBeRemoved
+    then shelly $ rm $ fromText $ T.pack fl
+    else return ()
+
+-- builder monad
+builder :: Builder ()
+builder = do
+    node "step0" 'create $ label .= "write something to a file"
+    node "step1" 'countWords $ label .= "word count"
+    node "step2" 'countChars $ label .= "character count"
+    node "step3" 'output $ label .= "print"
+    node "step4" 'cleanUp $ label .= "remove the file"
+
+    ["step0"] ~> "step1"
+    ["step0"] ~> "step2"
+    ["step1", "step2"] ~> "step3"
+    ["step3", "step0"] ~> "step4"
+
+---------------------------------------------------
+-- File 2: main.hs
+---------------------------------------------------
+{-# LANGUAGE TemplateHaskell #-}
+
+import System.Environment
+
+import qualified Functions as F
+import qualified Data.Text.Lazy.IO as T
+
+import Scientific.Workflow
+import Scientific.Workflow.Visualize
+
+buildWorkflow "wf" F.builder
+
+main :: IO ()
+main = do
+    (cmd:args) <- getArgs
+    case cmd of
+        "run" -> runWorkflow wf def
+        "view" -> T.putStrLn $ renderBuilder F.builder
+```
+
+The workflow can be visualized by running `runghc main.hs view | dot -Tpng > example.png`.
+
+![example](examples/example.png)
+
+To run the workflow, simply type `runghc main.hs run`. The program will create a directory to store results of each step. If being terminated prematurely, the program will use the saved data to continue from the last step.
diff --git a/SciFlow.cabal b/SciFlow.cabal
--- a/SciFlow.cabal
+++ b/SciFlow.cabal
@@ -1,8 +1,5 @@
--- Initial SciFlow.cabal generated by cabal init.  For further
--- documentation, see http://haskell.org/cabal/users-guide/
-
 name:                SciFlow
-version:             0.4.0
+version:             0.4.1
 synopsis:            Scientific workflow management system
 description:
   SciFlow is to help programmers design complex workflows
@@ -12,12 +9,17 @@
 license-file:        LICENSE
 author:              Kai Zhang
 maintainer:          kai@kzhang.org
-copyright:           (c) 2015 Kai Zhang
+copyright:           (c) 2016 Kai Zhang
 category:            Control
 build-type:          Simple
--- extra-source-files:
 cabal-version:       >=1.10
 
+extra-source-files:
+  README.md
+  examples/Functions.hs
+  examples/Main.hs
+  examples/example.png
+
 Flag Debug
   Description: Enable debug support
   Default:     False
@@ -50,6 +52,7 @@
     , th-lift
     , text
     , template-haskell
+    , transformers
     , containers
     , yaml
 
diff --git a/examples/Functions.hs b/examples/Functions.hs
new file mode 100644
--- /dev/null
+++ b/examples/Functions.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+module Functions
+    (builder) where
+
+import Control.Lens ((^.), (.=))
+import qualified Data.Text as T
+import Shelly hiding (FilePath)
+import Text.Printf (printf)
+
+import Scientific.Workflow
+
+create :: () -> IO FilePath
+create _ = do
+    writeFile "hello.txt" "hello world"
+    return "hello.txt"
+
+countWords :: FilePath -> IO Int
+countWords fl = do
+    content <- readFile fl
+    return $ length $ words content
+
+countChars :: FilePath -> IO Int
+countChars fl = do
+    content <- readFile fl
+    return $ sum $ map length $ words content
+
+output :: (Int, Int) -> IO Bool
+output (ws, cs) = do
+    putStrLn $ printf "Number of words: %d" ws
+    putStrLn $ printf "Number of characters: %d" cs
+    return True
+
+cleanUp :: (Bool, FilePath) -> IO ()
+cleanUp (toBeRemoved, fl) = if toBeRemoved
+    then shelly $ rm $ fromText $ T.pack fl
+    else return ()
+
+-- builder monad
+builder :: Builder ()
+builder = do
+    node "step0" 'create $ label .= "write something to a file"
+    node "step1" 'countWords $ label .= "word count"
+    node "step2" 'countChars $ label .= "character count"
+    node "step3" 'output $ label .= "print"
+    node "step4" 'cleanUp $ label .= "remove the file"
+
+    ["step0"] ~> "step1"
+    ["step0"] ~> "step2"
+    ["step1", "step2"] ~> "step3"
+    ["step3", "step0"] ~> "step4"
diff --git a/examples/Main.hs b/examples/Main.hs
new file mode 100644
--- /dev/null
+++ b/examples/Main.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+import System.Environment
+
+import qualified Functions as F
+import qualified Data.Text.Lazy.IO as T
+
+import Scientific.Workflow
+import Scientific.Workflow.Visualize
+
+buildWorkflow "wf" F.builder
+
+main :: IO ()
+main = do
+    (cmd:args) <- getArgs
+    case cmd of
+        "run" -> runWorkflow wf def
+        "view" -> T.putStrLn $ renderBuilder F.builder
diff --git a/examples/example.png b/examples/example.png
new file mode 100644
Binary files /dev/null and b/examples/example.png differ
diff --git a/src/Scientific/Workflow.hs b/src/Scientific/Workflow.hs
--- a/src/Scientific/Workflow.hs
+++ b/src/Scientific/Workflow.hs
@@ -6,9 +6,12 @@
     ) where
 
 import           Control.Monad.State
+import Control.Monad.Trans.Except
+import Control.Exception (displayException)
 
 import           Scientific.Workflow.Builder
 import           Scientific.Workflow.Types
+import System.IO
 
 runWorkflow :: [Workflow] -> State RunOpt () -> IO ()
 runWorkflow wfs setOpt = do
@@ -17,5 +20,9 @@
   where
     opt = execState setOpt defaultRunOpt
     f config (Workflow wf) = do
-        (_, config') <- runStateT (wf ()) config
-        return config'
+        result <- runExceptT $ runStateT (wf ()) config
+        case result of
+            Right (_, config') -> return config'
+            Left ex -> do
+                hPutStrLn stderr $ displayException ex
+                return config
diff --git a/src/Scientific/Workflow/Builder.hs b/src/Scientific/Workflow/Builder.hs
--- a/src/Scientific/Workflow/Builder.hs
+++ b/src/Scientific/Workflow/Builder.hs
@@ -17,6 +17,8 @@
     ) where
 
 import Control.Lens ((^.), (%~), _1, _2, _3, at, (.=))
+import Control.Exception (try, displayException)
+import Control.Monad.Trans.Except (throwE)
 import           Control.Monad.State
 import qualified Data.Text           as T
 import Data.Graph.Inductive.Graph
@@ -48,14 +50,23 @@
   lift t = [| T.pack $(T.lift $ T.unpack t) |]
 
 
+-- | The order of edges
 type EdgeOrd = Int
+
+-- | A computation node
 type Node = (PID, (ExpQ, Attribute))
+
+-- | Links between computational nodes
 type Edge = (PID, PID, EdgeOrd)
 
 type Builder = State ([Node], [Edge])
 
 -- | Declare a computational node
-node :: ToExpQ q => PID -> q -> State Attribute () -> Builder ()
+node :: ToExpQ q
+     => PID                  -- ^ node id
+     -> q                    -- ^ function
+     -> State Attribute ()   -- ^ Attribues
+     -> Builder ()
 node p fn setAttr = modify $ _1 %~ (newNode:)
   where
     attr = execState setAttr defaultAttribute
@@ -79,11 +90,14 @@
     f a t = link [a] t >> return t
 {-# INLINE path #-}
 
+-- | Build the workflow.
 buildWorkflow :: String
               -> Builder ()
               -> Q [Dec]
 buildWorkflow wfName b = mkWorkflow wfName $ mkDAG b
 
+-- | Build only a part of the workflow that has not been executed. This is used
+-- during development for fast compliation.
 buildWorkflowPart :: State RunOpt ()
                   -> String
                   -> Builder ()
@@ -100,7 +114,7 @@
     ks <- getKeys db
     pSt <- mapM (flip isFinished db) ks
     let pSts = M.fromList $ zipWith (\k s ->
-                 if s then (k, Finished) else (k, Scheduled)) ks pSt
+                 if s then (k, Success) else (k, Scheduled)) ks pSt
     return $ WorkflowState db pSts
 {-# INLINE getWorkflowState #-}
 
@@ -116,32 +130,39 @@
 
 type DAG = Gr Node EdgeOrd
 
+-- | Contruct a DAG representing the workflow
 mkDAG :: Builder () -> DAG
 mkDAG b = mkGraph ns' es'
   where
     ns' = map (\x -> (pid2nid $ fst x, x)) ns
     es' = map (\(fr, t, o) -> (pid2nid fr, pid2nid t, o)) es
     (ns, es) = execState b ([], [])
-    pid2nid p = M.findWithDefault (error "mkDAG") p m
+    pid2nid p = M.findWithDefault errMsg p m
       where
         m = M.fromListWithKey err $ zip (map fst ns) [0..]
         err k _ _ = error $ "multiple instances for: " ++ T.unpack k
+        errMsg = error $ "mkDAG: cannot identify node: " ++ T.unpack p
 {-# INLINE mkDAG #-}
 
+-- | Remove nodes that are executed before from a DAG.
 trimDAG :: WorkflowState -> DAG -> DAG
 trimDAG st dag = gmap revise gr
   where
-    revise c | done (c^._3._1) && null (c^._1) = _3._2._1 %~ e $ c
-            | otherwise = c
-      where e x = [| const undefined >=> $(x) |]
+    revise context@(linkTo, nd, lab, linkFrom)
+        | done (fst lab) && null linkTo = _3._2._1 %~ e $ context
+        | otherwise = context
+      where
+        e x = [| const undefined >=> $(x) |]
     gr = labnfilter f dag
       where
         f (i, (x,_)) = (not . done) x || any (not . done) children
           where children = map (fst . fromJust . lab dag) $ suc dag i
-    done x = getStatus x == Finished
-    getStatus x = M.findWithDefault Scheduled x $ st^.procStatus
+    done x = case M.lookup x (st^.procStatus) of
+        Just Success -> True
+        _ -> False
 {-# INLINE trimDAG #-}
 
+-- | Generate codes from a DAG
 mkWorkflow :: String -> DAG -> Q [Dec]
 mkWorkflow wfName dag = do
     decNode <- concat <$> mapM (f . snd) ns
@@ -168,17 +189,25 @@
 {-# INLINE mkWorkflow #-}
 
 mkProc :: Serializable b => PID -> (a -> IO b) -> (Processor a b)
-mkProc p f = \input -> do
+mkProc pid f = \input -> do
     st <- get
-    case M.findWithDefault Scheduled p (st^.procStatus) of
-        Finished -> lift $ readData p $ st^.db
+    case M.findWithDefault Scheduled pid (st^.procStatus) of
+        Fail ex -> lift $ throwE ex
+        Success -> do
+            r <- liftIO $ readData pid $ st^.db
+            return r
         Scheduled -> do
 #ifdef DEBUG
-            traceM $ "Running node: " ++ T.unpack p
+            traceM $ "Running node: " ++ T.unpack pid
 #endif
-            result <- lift $ f input
-            lift $ saveData p result $ st^.db
 
-            (procStatus . at p) .= Just Finished
-            return result
+            result <- liftIO $ try $ f input
+            case result of
+                Left ex -> do
+                    (procStatus . at pid) .= Just (Fail ex)
+                    lift $ throwE ex
+                Right r -> do
+                    liftIO $ saveData pid r $ st^.db
+                    (procStatus . at pid) .= Just Success
+                    return r
 {-# INLINE mkProc #-}
diff --git a/src/Scientific/Workflow/Types.hs b/src/Scientific/Workflow/Types.hs
--- a/src/Scientific/Workflow/Types.hs
+++ b/src/Scientific/Workflow/Types.hs
@@ -26,6 +26,8 @@
 
 import Control.Lens (makeLenses)
 import           Control.Monad.State
+import Control.Monad.Trans.Except (ExceptT)
+import Control.Exception (SomeException)
 import qualified Data.ByteString     as B
 import qualified Data.Map            as M
 import qualified Data.Text           as T
@@ -40,13 +42,16 @@
     serialize = encode
     deserialize = fromJust . decode
 
+-- | An abstract type representing the database used to store states of workflow
 data WorkflowDB  = WorkflowDB FilePath
 
+-- | The id of a node
 type PID = T.Text
 
-data ProcState = Finished
+-- | The state of a computation node
+data ProcState = Success
                | Scheduled
-    deriving (Eq)
+               | Fail SomeException
 
 data WorkflowState = WorkflowState
     { _db         :: WorkflowDB
@@ -55,7 +60,7 @@
 
 makeLenses ''WorkflowState
 
-type Processor a b = a -> StateT WorkflowState IO b
+type Processor a b = a -> StateT WorkflowState (ExceptT SomeException IO) b
 
 data Workflow where
     Workflow :: (Processor () o) -> Workflow
diff --git a/src/Scientific/Workflow/Visualize.hs b/src/Scientific/Workflow/Visualize.hs
--- a/src/Scientific/Workflow/Visualize.hs
+++ b/src/Scientific/Workflow/Visualize.hs
@@ -4,9 +4,7 @@
     ) where
 
 import Control.Lens
-import Scientific.Workflow.Types
-import           Shelly              (fromText, lsT, shelly, test_f, mkdir_p)
-import qualified Data.ByteString     as B
+import Scientific.Workflow.Types (label)
 import qualified Data.Text as T
 import qualified Data.Text.Lazy      as TL
 
@@ -17,6 +15,7 @@
 import Scientific.Workflow.Types (note)
 import Scientific.Workflow.Builder
 
+-- | Print the computation graph
 renderBuilder :: Builder () -> TL.Text
 renderBuilder b = renderDot . toDot $ graphToDot param dag
   where
