diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,13 @@
 # Revision history for taskwarrior
 
-## 0.3.1.0
+## 0.4.0.0
 
-* Add support with the JSON Format of taskwarrior 2.6.
+### Breaking Change
+* Change UDA Type from HashMap to ordered Map.
+
+### Maintenance
+* Update to aeson 2.0.
+* Improve test.nix
 
 ## 0.3.0.0
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -35,10 +35,10 @@
 
 Also I'd be curious to know what you use this library for.
 
-This project uses brittany in default configuration as code formatter.
-The tests on github will check for hlints, missing docs and unapplied formatting.
+This project uses fourmolu as code formatter.
 
 ## Help & Contact
 
-You can always open an issue on GitHub. You can also ask in `#haskell-taskwarrior` on freenode irc. If you don‘t have an irc client you can log in via [the webchat](https://webchat.freenode.net/#haskell-taskwarrior).
-Shooting @maralorn a mail is also an option. But of course that won’t be public and therefore not help anyone else.
+You can always open an issue on GitHub. Contacting @maralorn:maralorn.de on
+matrix is also an option. But of course that won’t be public and therefore not
+help anyone else.
diff --git a/src/Taskwarrior/Task.hs b/src/Taskwarrior/Task.hs
--- a/src/Taskwarrior/Task.hs
+++ b/src/Taskwarrior/Task.hs
@@ -30,12 +30,14 @@
   withText,
   (.:),
   (.:?),
-  (.=), withArray
+  (.=),
  )
 import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Key as Key
+import qualified Data.Aeson.KeyMap as KeyMap
 import qualified Data.Aeson.Types as Aeson.Types
 import qualified Data.Foldable as Foldable
-import qualified Data.HashMap.Strict as HashMap
+import qualified Data.Map.Strict as Map
 import Data.Maybe (fromMaybe)
 import qualified Data.Maybe as Maybe
 import qualified Data.Semigroup as Semigroup
@@ -56,14 +58,12 @@
 import qualified Taskwarrior.Status as Status
 import qualified Taskwarrior.Time as Time
 import Taskwarrior.UDA (UDA)
-import Data.Foldable (toList)
-import Control.Applicative ((<|>))
 
 {- | A 'Task' represents a task from taskwarrior.
  The specification demands, that the existence of some fields is dependent on the status of the task.
  Those fields are therefore bundled in 'Status' as a sum-type.
 
- All fields in an imported task which are not part of the specification will be put in the 'UDA' (user defined attributes) 'Data.HashMap.Strict.HashMap'.
+ All fields in an imported task which are not part of the specification will be put in the 'UDA' (user defined attributes) 'Data.Map.Strict.Map Data.Text.Text'.
 
  Since the json can have multiple semantically equivalent representations of a task first serializing and then deserializing is not identity.
  But deserializing and then serializing should be. (Thus making serializing and deserializing idempotent.)
@@ -122,7 +122,7 @@
 instance FromJSON Task where
   parseJSON = withObject "Task" $ \object -> do
     let parseTimeFromFieldMay = parseFromFieldWithMay Time.parse object
-        uda = HashMap.filterWithKey (\k _ -> k `notElem` reservedKeys) object
+        uda = Map.filterWithKey (\k _ -> k `notElem` reservedKeys) . Map.mapKeys Key.toText $ KeyMap.toMap object
     status <- Status.parseFromObject object
     recurringChild <- RecurringChild.parseFromObjectMay object
     uuid <- object .: "uuid"
@@ -144,7 +144,7 @@
       maybe
         (pure mempty)
         parseUuidList
-        (HashMap.lookup "depends" object)
+        (KeyMap.lookup (Key.fromText "depends") object)
     tags <- Foldable.fold <$> object .:? "tags"
     urgency <- fromMaybe 0 <$> object .:? "urgency"
     pure Task{until = until_, ..}
@@ -155,15 +155,14 @@
   Text ->
   Aeson.Types.Parser (Maybe a)
 parseFromFieldWithMay parser object name =
-  traverse parser (HashMap.lookup name object)
+  traverse parser (KeyMap.lookup (Key.fromText name) object)
 
 parseUuidList :: Aeson.Value -> Aeson.Types.Parser (Set UUID)
-parseUuidList val =
-  (withArray "Array of uuid strings" $ fmap Set.fromList . mapM parseJSON . toList) val <|>
-  (withText "Comma separated list of uuids" $
+parseUuidList =
+  withText "Text" $
     fmap Set.fromList
       . mapM (parseJSON . Aeson.String)
-      . Text.splitOn ",") val
+      . Text.splitOn ","
 
 instance ToJSON Task where
   toJSON Task{until = until_, ..} =
@@ -197,7 +196,7 @@
               . Set.toList
           )
         <> ifNotNullSet tags ("tags" .=)
-        <> HashMap.toList uda
+        <> Map.toList (Map.mapKeys Key.fromText uda)
 
 ifNotNullSet :: (Ord b) => Set b -> (Set b -> a) -> [a]
 ifNotNullSet set f =
@@ -205,7 +204,7 @@
   )
     [f set]
 
--- | Makes a Task with the given mandatory fields uuid, entry time and description. See createTask for a non-pure version which needs less parameters.
+-- | Makes a fresh Task with the given mandatory fields uuid, entry time and description. See createTask for a non-pure version which needs less parameters.
 makeTask :: UUID -> UTCTime -> Text -> Task
 makeTask uuid entry description =
   Task
@@ -226,5 +225,5 @@
     , depends = mempty
     , tags = mempty
     , urgency = 0
-    , uda = HashMap.empty
+    , uda = Map.empty
     }
diff --git a/src/Taskwarrior/UDA.hs b/src/Taskwarrior/UDA.hs
--- a/src/Taskwarrior/UDA.hs
+++ b/src/Taskwarrior/UDA.hs
@@ -1,11 +1,11 @@
--- | User defined attributes are stored in a 'HashMap' from 'Text' to json 'Value's because we have no type information about them.
+-- | User defined attributes are stored in a 'Map' from 'Text' to json 'Value's because we have no type information about them.
 module Taskwarrior.UDA (
   UDA,
 ) where
 
 import Data.Aeson (Value)
-import Data.HashMap.Strict (HashMap)
+import Data.Map.Strict (Map)
 import Data.Text (Text)
 
 -- | A field will in practice only be a number or a string.
-type UDA = HashMap Text Value
+type UDA = Map Text Value
diff --git a/taskwarrior.cabal b/taskwarrior.cabal
--- a/taskwarrior.cabal
+++ b/taskwarrior.cabal
@@ -4,7 +4,7 @@
 -- PVP summary:     +-+------- breaking API changes
 --                  | | +----- non-breaking API additions
 --                  | | | +--- code changes with no API change
-version: 0.3.1.0
+version: 0.4.0.0
 synopsis:           Types and aeson instances for taskwarrior tasks
 description:
   Types and aeson instances for the https://taskwarrior.org task import/export feature
@@ -45,7 +45,7 @@
     StrictData
 
   build-depends:
-    , aeson                 >=1.4.2.0  && < 1.6
+    , aeson                 >=2.0.0.0  && < 2.1
     , base                  >=4.11     && < 4.16
     , bytestring            >=0.10.8.2 && < 0.12
     , containers            >=0.5.0.0  && < 0.7
@@ -53,7 +53,6 @@
     , random                >=1.1      && < 1.3
     , text                  ^>=1.2.3.0
     , time                  >=1.8.0.2  && < 1.13
-    , unordered-containers  ^>=0.2.9.0
     , uuid                  ^>=1.3.13
 
   hs-source-dirs:     src
@@ -69,13 +68,13 @@
   build-depends:
     , aeson
     , base
+    , containers
     , hspec                 >=2.7.1 && < 2.9
     , QuickCheck            >=2.13.2 && <2.15
     , quickcheck-instances  ^>=0.3.22
     , taskwarrior
     , text
     , time
-    , unordered-containers
     , uuid
 
   default-language:   Haskell2010
diff --git a/test/TaskSpec.hs b/test/TaskSpec.hs
--- a/test/TaskSpec.hs
+++ b/test/TaskSpec.hs
@@ -8,7 +8,7 @@
 import Prelude hiding (id)
 
 import Data.Aeson
-import qualified Data.HashMap.Strict as HashMap
+import qualified Data.Map.Strict as Map
 import Data.Time
 import Taskwarrior.Annotation
 import Taskwarrior.Mask
@@ -91,5 +91,5 @@
     depends <- arbitrary
     tags <- arbitrary
     urgency <- arbitrary
-    uda <- HashMap.fromList . fmap (second String) <$> arbitrary
+    uda <- Map.fromList . fmap (second String) <$> arbitrary
     pure Task{until = until_, ..}
