diff --git a/lib/type_id.ex b/lib/type_id.ex
index b4d7ea6..5ec44d0 100644
--- a/lib/type_id.ex
+++ b/lib/type_id.ex
@@ -104,7 +104,7 @@ defmodule TypeID do
   end
 
   defp validate_prefix!(prefix) do
-    unless prefix =~ ~r/[a-z]{0,63}/ do
+    unless prefix =~ ~r/^[a-z]{0,63}$/ do
       raise ArgumentError, "invalid prefix: #{prefix}. prefix should match [a-z]{0,63}"
     end
 
diff --git a/lib/type_id/base32.ex b/lib/type_id/base32.ex
deleted file mode 100644
index 1bacc25..0000000
--- a/lib/type_id/base32.ex
+++ /dev/null
@@ -1,230 +0,0 @@
-defmodule TypeID.Base32 do
-  @moduledoc false
-  import Bitwise
-
-  # Implements base 32 encoding using the a lowercase crockford alphabet
-  # https://www.crockford.com/base32.html
-
-  # Borrows heavily from the core `Base` module's implementation
-
-  crockford_alphabet = ~c"0123456789ABCDEFGHJKMNPQRSTVWXYZ"
-
-  to_lower_enc = &Enum.map(&1, fn c -> if c in ?A..?Z, do: c - ?A + ?a, else: c end)
-  to_lower_dec =
-    &Enum.map(&1, fn {encoding, value} = pair ->
-      if encoding in ?A..?Z do
-        {encoding - ?A + ?a, value}
-      else
-        pair
-      end
-    end)
-
-  lower = to_lower_enc.(crockford_alphabet)
-
-
-  encoded = for e1 <- lower, e2 <- lower, do: bsl(e1, 8) + e2
-
-  to_decode_list = fn alphabet ->
-    alphabet = Enum.sort(alphabet)
-    map = Map.new(alphabet)
-    {min, _} = List.first(alphabet)
-    {max, _} = List.last(alphabet)
-    {min, Enum.map(min..max, &map[&1])}
-  end
-
-  {min, decoded} =
-    lower
-    |> Enum.with_index()
-    |> to_lower_dec.()
-    |> to_decode_list.()
-
-  @spec encode(binary()) :: binary()
-  def encode(data) when is_binary(data) do
-    do_encode(data, "")
-  end
-
-  @spec decode(binary()) :: {:ok, binary()} | :error
-  def decode(string) when is_binary(string) do
-    {:ok, decode!(string)}
-  rescue
-    ArgumentError -> :error
-  end
-
-  @spec decode!(binary()) :: binary() | no_return()
-  def decode!(string) when is_binary(string) do
-    do_decode!(string)
-  end
-
-  @compile {:inline, [do_encode: 1]}
-  defp do_encode(byte) do
-    elem({unquote_splicing(encoded)}, byte)
-  end
-
-  defp do_encode(<<c1::10, c2::10, c3::10, c4::10, rest::binary>>, acc) do
-    do_encode(
-      rest,
-      <<
-        acc::binary,
-        do_encode(c1)::16,
-        do_encode(c2)::16,
-        do_encode(c3)::16,
-        do_encode(c4)::16
-      >>
-    )
-  end
-
-  defp do_encode(<<c1::10, c2::10, c3::10, c4::2>>, acc) do
-    <<
-      acc::binary,
-      do_encode(c1)::16,
-      do_encode(c2)::16,
-      do_encode(c3)::16,
-      c4 |> bsl(3) |> do_encode() |> band(0x00FF)::8
-    >>
-  end
-
-  defp do_encode(<<c1::10, c2::10, c3::4>>, acc) do
-    <<
-      acc::binary,
-      do_encode(c1)::16,
-      do_encode(c2)::16,
-      c3 |> bsl(1) |> do_encode() |> band(0x00FF)::8
-    >>
-  end
-
-  defp do_encode(<<c1::10, c2::6>>, acc) do
-    <<
-      acc::binary,
-      do_encode(c1)::16,
-      c2 |> bsl(4) |> do_encode()::16
-    >>
-  end
-
-  defp do_encode(<<c1::8>>, acc) do
-    <<acc::binary, c1 |> bsl(2) |> do_encode()::16>>
-  end
-
-  defp do_encode(<<>>, acc) do
-    acc
-  end
-
-  defp do_decode!(<<>>), do: <<>>
-
-  defp do_decode!(string) when is_binary(string) do
-    segs = div(byte_size(string) + 7, 8) - 1
-    <<main::size(segs)-binary-unit(64), rest::binary>> = string
-
-    main =
-      for <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8 <- main>>, into: <<>> do
-        <<
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          do_decode!(c4)::5,
-          do_decode!(c5)::5,
-          do_decode!(c6)::5,
-          do_decode!(c7)::5,
-          do_decode!(c8)::5
-        >>
-      end
-
-    case rest do
-      <<c1::8, c2::8, ?=, ?=, ?=, ?=, ?=, ?=>> ->
-        <<main::bits, do_decode!(c1)::5, bsr(do_decode!(c2), 2)::3>>
-
-      <<c1::8, c2::8, c3::8, c4::8, ?=, ?=, ?=, ?=>> ->
-        <<
-          main::bits,
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          bsr(do_decode!(c4), 4)::1
-        >>
-
-      <<c1::8, c2::8, c3::8, c4::8, c5::8, ?=, ?=, ?=>> ->
-        <<
-          main::bits,
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          do_decode!(c4)::5,
-          bsr(do_decode!(c5), 1)::4
-        >>
-
-      <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, ?=>> ->
-        <<
-          main::bits,
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          do_decode!(c4)::5,
-          do_decode!(c5)::5,
-          do_decode!(c6)::5,
-          bsr(do_decode!(c7), 3)::2
-        >>
-
-      <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8>> ->
-        <<
-          main::bits,
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          do_decode!(c4)::5,
-          do_decode!(c5)::5,
-          do_decode!(c6)::5,
-          do_decode!(c7)::5,
-          do_decode!(c8)::5
-        >>
-
-      <<c1::8, c2::8>> ->
-        <<main::bits, do_decode!(c1)::5, bsr(do_decode!(c2), 2)::3>>
-
-      <<c1::8, c2::8, c3::8, c4::8>> ->
-        <<
-          main::bits,
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          bsr(do_decode!(c4), 4)::1
-        >>
-
-      <<c1::8, c2::8, c3::8, c4::8, c5::8>> ->
-        <<
-          main::bits,
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          do_decode!(c4)::5,
-          bsr(do_decode!(c5), 1)::4
-        >>
-
-      <<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8>> ->
-        <<
-          main::bits,
-          do_decode!(c1)::5,
-          do_decode!(c2)::5,
-          do_decode!(c3)::5,
-          do_decode!(c4)::5,
-          do_decode!(c5)::5,
-          do_decode!(c6)::5,
-          bsr(do_decode!(c7), 3)::2
-        >>
-    end
-  end
-
-  defp do_decode!(char) do
-    try do
-      elem({unquote_splicing(decoded)}, char - unquote(min))
-    rescue
-      _ -> bad_character!(char)
-    else
-      nil -> bad_character!(char)
-      char -> char
-    end
-  end
-
-  defp bad_character!(byte) do
-    raise ArgumentError,
-          "non-alphabet character found: #{inspect(<<byte>>, binaries: :as_strings)} (byte #{byte})"
-  end
-end
diff --git a/lib/type_id/base_32.ex b/lib/type_id/base_32.ex
new file mode 100644
index 0000000..34804f5
--- /dev/null
+++ b/lib/type_id/base_32.ex
@@ -0,0 +1,58 @@
+defmodule TypeID.Base32 do
+  @moduledoc false
+
+  # Implements base 32 encoding using the a lowercase crockford alphabet
+  # https://www.crockford.com/base32.html
+
+  crockford_alphabet = ~c"0123456789abcdefghjkmnpqrstvwxyz"
+
+  @spec encode(binary()) :: binary()
+  def encode(
+        <<c1::3, c2::5, c3::5, c4::5, c5::5, c6::5, c7::5, c8::5, c9::5, c10::5, c11::5, c12::5,
+          c13::5, c14::5, c15::5, c16::5, c17::5, c18::5, c19::5, c20::5, c21::5, c22::5, c23::5,
+          c24::5, c25::5, c26::5>>
+      ) do
+    <<do_encode(c1)::8, do_encode(c2)::8, do_encode(c3)::8, do_encode(c4)::8, do_encode(c5)::8,
+      do_encode(c6)::8, do_encode(c7)::8, do_encode(c8)::8, do_encode(c9)::8, do_encode(c10)::8,
+      do_encode(c11)::8, do_encode(c12)::8, do_encode(c13)::8, do_encode(c14)::8,
+      do_encode(c15)::8, do_encode(c16)::8, do_encode(c17)::8, do_encode(c18)::8,
+      do_encode(c19)::8, do_encode(c20)::8, do_encode(c21)::8, do_encode(c22)::8,
+      do_encode(c23)::8, do_encode(c24)::8, do_encode(c25)::8, do_encode(c26)::8>>
+  end
+
+  @spec decode(binary()) :: {:ok, binary()} | :error
+  def decode(string) when is_binary(string) do
+    {:ok, decode!(string)}
+  rescue
+    ArgumentError -> :error
+  end
+
+  @spec decode!(binary()) :: binary() | no_return()
+  def decode!(
+        <<c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19,
+          c20, c21, c22, c23, c24, c25, c26>>
+      ) do
+    <<do_decode(c1)::3, do_decode(c2)::5, do_decode(c3)::5, do_decode(c4)::5, do_decode(c5)::5,
+      do_decode(c6)::5, do_decode(c7)::5, do_decode(c8)::5, do_decode(c9)::5, do_decode(c10)::5,
+      do_decode(c11)::5, do_decode(c12)::5, do_decode(c13)::5, do_decode(c14)::5,
+      do_decode(c15)::5, do_decode(c16)::5, do_decode(c17)::5, do_decode(c18)::5,
+      do_decode(c19)::5, do_decode(c20)::5, do_decode(c21)::5, do_decode(c22)::5,
+      do_decode(c23)::5, do_decode(c24)::5, do_decode(c25)::5, do_decode(c26)::5>>
+  end
+
+  @compile {:inline, [do_encode: 1]}
+  defp do_encode(byte) do
+    elem({unquote_splicing(crockford_alphabet)}, byte)
+  end
+
+  for {char, byte} <- Enum.with_index(crockford_alphabet) do
+    defp do_decode(unquote(char)), do: unquote(byte)
+  end
+
+  defp do_decode(char), do: bad_character!(char)
+
+  defp bad_character!(byte) do
+    raise ArgumentError,
+          "non-alphabet character found: #{inspect(<<byte>>, binaries: :as_strings)} (byte #{byte})"
+  end
+end
diff --git a/test/type_id_test.exs b/test/type_id_test.exs
index 3b76643..daa2676 100644
--- a/test/type_id_test.exs
+++ b/test/type_id_test.exs
@@ -26,12 +26,40 @@ defmodule TypeIDTest do
   describe "serialization" do
     test "to_string/1 and from_string!/1 are idempotent" do
       tid1 = TypeID.from_string!("test_01h44had5rfswbvpc383ktj0aa")
+
       tid2 =
         tid1
         |> TypeID.to_string()
         |> TypeID.from_string!()
+
       assert tid1 == tid2
     end
+
+    test "from_string/1" do
+      assert {:ok, _} = TypeID.from_string("test_01h44xf16gf47v3s4khvc3c5ga")
+      assert :error == TypeID.from_string("-invalid_01h44xf16gf47v3s4khvc3c5ga")
+    end
+
+    test "from!/2 and from/2 validates the prefix" do
+      assert_raise ArgumentError, fn ->
+        TypeID.from!("-invalid-prefix-", "01h44had5rfswbvpc383ktj0aa")
+      end
+
+      assert :error == TypeID.from("-invalid-prefix-", "01h44had5rfswbvpc383ktj0aa")
+    end
+
+    test "from!/2 and from/2 validate the suffix" do
+      assert_raise ArgumentError, fn ->
+        TypeID.from!("test", "0ih44had5rfswbvpc383ktj0aa")
+      end
+
+      assert :error == TypeID.from("test", "0ih44had5rfswbvpc383ktj0aa")
+    end
   end
 
+  test "verification" do
+    tid = TypeID.from_string!("test_01h44yssjcf5daefvfr0yb70s8")
+    assert "test" == TypeID.type(tid)
+    assert "018909ec-e64c-795a-a73f-6fc03cb38328" == TypeID.uuid(tid)
+  end
 end