diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,63 @@
-## [_Unreleased_](https://github.com/freckle/freckle-prelude/compare/v0.0.4.1...main)
+## [_Unreleased_](https://github.com/freckle/freckle-prelude/compare/v0.1.0.0...main)
+
+## [v0.1.0.0](https://github.com/freckle/freckle-prelude/compare/v0.0.4.1...v0.1.0.0)
+
+- Remove `throwString :: (MonadIO m, HasCallStack) => String -> m a`
+- Remove `fromJustNoteM :: (MonadIO m, HasCallStack) => String -> Maybe a -> m a`
+- Remove `error :: HasCallStack => String -> a`
+- Remove `errorWithoutStackTrace :: String -> a`
+- Remove `fail :: MonadFail m => String -> m a` (the `MonadFail` class is still exported, without its method)
+- Add `throw :: Exception e => e -> a`, re-exported from `Control.Exception` in `base`
+
+Throwing exceptions from strings is discouraged. Instead, define a dedicated
+exception type and throw it with
+`throwM :: (Exception e, MonadIO m, HasCallStack) => e -> m a` in a monadic
+context, or with `throw` in pure code. Some uses of `fail` are legitimate, such
+as writing an aeson `Parser`. For those, import `fail` explicitly from
+`Control.Monad.Fail`.
+
+Defining a dedicated exception type takes a few lines. Where you previously
+threw a constant string, a type with no fields is enough:
+
+```haskell
+-- Before
+throwString "Default grading scale is missing"
+
+-- After
+throwM DefaultGradingScaleMissing
+
+data DefaultGradingScaleMissing = DefaultGradingScaleMissing
+  deriving stock (Show)
+  deriving anyclass (Exception)
+```
+
+Where the string was built by concatenating values, those values become fields
+of the exception instead:
+
+```haskell
+-- Before
+throwString $ "User " <> show userId <> " is not in classroom " <> show classroomId
+
+-- After
+throwM UserNotInClassroom {userId, classroomId}
+
+data UserNotInClassroom = UserNotInClassroom
+  { userId :: UserId
+  , classroomId :: ClassroomId
+  }
+  deriving stock (Show)
+  deriving anyclass (Exception)
+```
+
+If you want to keep a human-readable message rather than the derived `Show`
+output, remove `deriving anyclass (Exception)` and define the instance
+yourself, with `displayException`:
+
+```haskell
+instance Exception UserNotInClassroom where
+  displayException e =
+    "User " <> show e.userId <> " is not in classroom " <> show e.classroomId
+```
 
 ## [v0.0.4.1](https://github.com/freckle/freckle-prelude/tree/v0.0.4.1)
 
diff --git a/freckle-prelude.cabal b/freckle-prelude.cabal
--- a/freckle-prelude.cabal
+++ b/freckle-prelude.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.18
 name:               freckle-prelude
-version:            0.0.4.1
+version:            0.1.0.0
 license:            MIT
 license-file:       LICENSE
 maintainer:         Freckle Education
diff --git a/library/Freckle/App/Prelude.hs b/library/Freckle/App/Prelude.hs
--- a/library/Freckle/App/Prelude.hs
+++ b/library/Freckle/App/Prelude.hs
@@ -92,8 +92,7 @@
 
     -- * Exceptions
   , throwM
-  , throwString
-  , fromJustNoteM
+  , throw
   , catch
   , catchJust
   , catches
@@ -113,6 +112,9 @@
 
 import Prelude hiding
   ( cycle
+  , error
+  , errorWithoutStackTrace
+  , fail
   , foldl1
   , foldr1
   , head
@@ -131,6 +133,7 @@
 -- Commonly used types (and their commonly used functions)
 
 import Control.Applicative (Alternative, liftA2, optional, (<|>))
+import Control.Exception (throw)
 import Control.Monad.IO.Class (liftIO)
 import Control.Monad.Primitive (PrimMonad)
 import Control.Monad.Reader (MonadReader, ReaderT)
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
 name: freckle-prelude
-version: 0.0.4.1
+version: 0.1.0.0
 maintainer: Freckle Education
 category: Prelude
 github: freckle/freckle-prelude
