diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
 # Revision history for schemas
-## 0.4 -- 2019-12-20
+## 0.4.0.1 -- 2020-01-01
+* Replace breadth-first search with a greedy depth-first search
+
+## 0.4.0 -- 2019-12-20
 * Handle non-termination as an effect in decoding
 * Union api made more consistent across tagged and non tagged
 * (internal) More principled TypedSchema ADT based on Sum Profunctors
@@ -9,10 +12,6 @@
 
 ## 0.3.0.2 --  2019-10-29
 * Show circular schemas
-
-## 0.4.0
-* 'oneOf' and 'liftPrism' now have a better type signature
-* 'UnionTag' renamed to 'UnionAlt'
 
 ## 0.3.0.1 --  2019-10-25
 * Fix a bug that made OpenApi2 generation diverge.
diff --git a/schemas.cabal b/schemas.cabal
--- a/schemas.cabal
+++ b/schemas.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.0
 name:                schemas
-version:             0.4.0.0
+version:             0.4.0.1
 synopsis:            schema guided serialization
 description:
   Schemas is a Haskell library for serializing and deserializing data in JSON.
@@ -13,11 +13,11 @@
   schemas, and the source is a subtype of the target, source values can be encoded
   in the target schema.
   .
-  The library also supports @oneOf@ and @allOf@ schemas, which extend the
+  The library also supports @oneOf@ schemas, which extend the
   range of versioning changes that can be supported automatically without
   resorting to explicit versions and conversion functions.
   .
-  A type class @HasSchema@ is provided purely for convenience, but none of the
+  A type class @HasSchema@ is provided for convenience, but none of the
   core functions in the library rely on type classes.
   .
   Schemas can be derived generically using @generics-sop@, although most of the
@@ -46,7 +46,6 @@
     Schemas.Class
     Schemas.Internal
     Schemas.OpenApi2
-    Schemas.Delay
     Schemas.Attempt
     Schemas.SOP
     Schemas.Untyped
diff --git a/src/Schemas/Delay.hs b/src/Schemas/Delay.hs
deleted file mode 100644
--- a/src/Schemas/Delay.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-{-# LANGUAGE DeriveTraversable #-}
-{-# LANGUAGE DeriveFoldable #-}
-{-# LANGUAGE DeriveFunctor #-}
-module Schemas.Delay where
-
-import Control.Monad.Trans.Iter
-import Numeric.Natural
-
-type DelayT m = IterT m
-type Delay    = Iter
-
-lift :: Monad m => Iter a -> IterT m a
-lift = liftIter
-
-runDelay :: Monad m => Natural -> DelayT m a -> m (Maybe a)
-runDelay n = retract . cutoff (fromIntegral n)
-
-runDelayUnbounded :: Monad m => DelayT m a -> m a
-runDelayUnbounded = retract
diff --git a/src/Schemas/Internal.hs b/src/Schemas/Internal.hs
--- a/src/Schemas/Internal.hs
+++ b/src/Schemas/Internal.hs
@@ -208,13 +208,14 @@
   dimap f g (RequiredAp name sc) = RequiredAp name (dimap f g sc)
   dimap f g (OptionalAp name sc v) = OptionalAp name (dimap f g sc) (g v)
 
--- | An 'Alternative' profunctor for defining record schemas with versioning
+-- | An 'Alternative' profunctor for defining record schemas with versioning.
 --
 -- @
 --  schemaPerson = Person
 --             \<$\> (field "name" name \<|\> field "full name" name)
 --             \<*\> (field "age" age \<|\> pure -1)
 -- @
+-- Alternatives are searched greedily in a top-down order.
 newtype RecordFields from a = RecordFields {getRecordFields :: Alt (RecordField from) a}
   deriving newtype (Alternative, Applicative, Functor, Monoid, Semigroup)
 
@@ -296,6 +297,7 @@
 altWith sc p = UnionAlt p sc
 
 -- | Discriminated unions that record the name of the chosen constructor in the schema
+--
 -- @
 --   data Education = Degree Text | PhD Text | NoEducation
 --
@@ -311,6 +313,8 @@
   go [] = TEmpty absurd (error "incomplete union definition")
 
 -- | Undiscriminated union that do not record the name of the constructor in the schema
+--
+-- @
 --   data Education = Degree Text | PhD Text | NoEducation
 --
 --   schemaEducation = oneOf
@@ -319,6 +323,7 @@
 --     , alt #_PhD
 --     ]
 --   @
+-- Alternatives are searched greedily in a top-down order.
 oneOf :: (NonEmpty (UnionAlt from)) -> TypedSchema from
 oneOf (a :| rest) = go (a:rest) where
   go (UnionAlt p sc : rest) = liftPrism p sc $ go rest
@@ -391,18 +396,7 @@
 
 instance (MonadPlus m) => Alternative (IterAltT m) where
   empty = IterAlt (lift empty)
-  IterAlt (IterT ma) <|> IterAlt (IterT mb) = IterAlt $ IterT $
-    do a <- optional ma
-       case a of
-         Nothing -> mb
-         Just (Left done) -> pure (Left done)
-         Just (Right more_a) -> do
-           b <- optional mb
-           case b of
-             Nothing -> pure $ Right more_a
-             Just (Left done) -> pure (Left done)
-             Just (Right more_b) -> pure $ Right (more_a <|> more_b)
-
+  IterAlt a <|> IterAlt b = IterAlt $ IterT $ runIterT a <|> runIterT b
 
 runDelay :: Monad m => Natural -> IterAltT m a -> m (Maybe a)
 runDelay n = retract . cutoff (fromIntegral n) . runIterAlt
@@ -644,7 +638,9 @@
   go env ctx (TOneOf sc sc' tof _) src = do
     let parserL = runAttempt $ (Left <.>) <$> go env ctx sc src
     let parserR = runAttempt $ (Right <.>) <$> go env ctx sc' src
-    case partitionEithers [parserL, parserR] of
+    -- parserR comes first
+    -- This is because of how liftPrism and oneOf work
+    case partitionEithers [parserR, parserL] of
       (ee, []) -> failWith ctx (AllAlternativesFailed (concat ee))
       (_ , pp) -> do
         pure $ \x -> tof <$> asum (map ($ x) pp)
