   end
 
   defp encode_fields(fields, offset) do
-    fields =
-      for {name, value} <- fields do
-        type  = infer_type_from_term(value)
-        field = named_field(name: name, data_type: type)
-        value = encode_type(value, type)
-        {field, value}
-      end
-
-    fields_offset = Enum.map(fields, fn({field, _}) ->
-      encode_header_field(field) |> IO.iodata_length
-    end) |> Enum.sum
-
+    fields = Enum.map(fields, &encode_field_value/1)
+    offset = offset + header_offset(fields)
+
+    {fields, values, _} = Enum.reduce fields, {[], [], offset}, fn(%Field{} = field, {fs, vs, index}) ->
+      encoded_field = %{field | pointer_to_data: index} |> encode_field_for_header
+      index         = index + IO.iodata_length(field.encoded_value)
+
+      {[encoded_field|fs], [field.encoded_value|vs], index}
+    end
+
+    [Enum.reverse(fields), 0, Enum.reverse(values)]
+  end
+
+  # Returns the length of the header based on the list of fields.
+  defp header_offset(fields) do
     # The last +1 is for the `0` that signals the end of the header.
-    offset = offset + fields_offset + 1
-
-    {fields, values, _} = Enum.reduce fields, {[], [], 0}, fn({field, value}, {fs, vs, index}) ->
-      ptr = offset + index
-      encoded_field = named_field(field, data_ptr: ptr) |> encode_header_field
-
-      index = index + IO.iodata_length(value)
-
-      {[encoded_field|fs], [value|vs], index}
-    end
-
-    fields = Enum.reverse(fields)
-    values = Enum.reverse(values)
-
-    [fields, 0, values]
-  end
-
-  defp encode_header_field(field) when is_record(field, :named_field) do
-    named_field(name: name, data_ptr: ptr, data_type: type) = field
-
+    fields
+    |> Enum.map(&(&1 |> encode_field_for_header |> IO.iodata_length))
+    |> Enum.sum
+    |> +(1)
+  end
+
+  # Returns the given `%Field{}` with the `:encoded_value` field set to the
+  # result of encoding the `:value` field.
+  defp encode_field_value(%Field{type: type, value: value} = field) do
+    %{field | encoded_value: encode_type(value, type)}
+  end
+
+  # Encodes the given `%Field{}` for the header, i.e., just the field
+  # representation and not the value (name, pointer to data, type). Returns
+  # iodata.
+  defp encode_field_for_header(%Field{pointer_to_data: ptr, type: type, name: name} = field) do
     if is_nil(ptr) do
       ptr = 0
     end
 
-    [encode_type(name, type), <<ptr :: 32-signed>>, type_to_int(type)]
+    [encode_type(name, :string), <<ptr :: 32-signed>>, type_to_int(type)]
   end
 
   # Encodes an instance of `type`. Returns an iodata instead of a binary.


  defrecordp :map_key, [:key, :data_type, :data_ptr]

  @doc """
  Parses a binary-serialized record.
  """
  @spec decode(binary) :: {non_neg_integer, String.t, %{}}
  def decode(data) do
    <<_version, rest :: binary>> = data
    {record, <<>>} = decode_document(rest)
    record
  end

  @doc """
  Serializes a record using the binary serialization protocol.

  `class_name` is a string containing the class name of the record being
  encoded. `fields` is a list of fields.
  """
  def encode({class_name, fields}) do
    version_and_class = [0, encode_type(class_name || "", :string)]

  end

  defp field_name(field) when is_record(field, :named_field), do: named_field(field, :name)

  # http://orientdb.com/docs/last/Types.html
  @types [
