Q:

Clojure defrecord serialization ClassNotFoundException

I was trying to serialise one of my records to a human readable format. While serialising using Java serialiser worked fine I am trying to use print-dup. The problem I am facing is that while writing the record goes fine reading the record results in  clojure.lang.LispReader$ReaderException: java.lang.ClassNotFoundException: common.dummy.Doodh. Am I messing up the namespaces or something?
Please note that this is not a problem with Java serialisation.
Code below in the simplest form
(ns common.dummy)

   (defrecord Doodh [id name])

   (defn output [filename obj]
    (def trr(map->Doodh {:id "moooh" :name "Cows"}))
    (def my-string (binding [*print-dup* true] (pr-str trr)))
    (spit filename my-string)
   )

   (defn pull [filename]
     (def my-data (with-in-str (slurp filename) (read)))
     (println my-data)
   )

text file contents:
#common.dummy.Doodh["moooh", "Cows"]

A:

Don't use def inside function definitions. When you are using def, you create a var in your namespace and possibly manipulate it as a side-effect with every function call. Use let-blocks.
If you want to save Clojure data structures in a file, use clojure.edn. It is safe (e. g. without your knowledge, no functions defined in the file will be invoked) but it allows to enable custom readers (see more below). 
A type defined with defrecord can be printed in a (Clojure-reader-)readable way using pr-str (thanks to @A. Webb for noting that). In your example, I don't see why you would not stick to a hash-map in the first place, but if you really need a defrecord here, you may convert it into a readable string before your write it to the file.
(defrecord Doodh [id name])

(defn output [filename obj]
  (spit filename (pr-str obj))

(defn pull [filename]
  (with-in-str (slurp filename)
               (read)))

This way of doing it has several disadvantages. 

Using read makes your code vulnerable to function calls in the slurped files (like #=(java.lang.System/exit 0)).
An exception will be thrown when the file at filename is empty.
Finally your saved file will become incompatible to your code when you move your defrecord declaration to another namespace. 
All three disadvantages can be avoided by using the edn-reader.

Using a custom reader with EDN

We extend our type Doodh by implementing the toString method of the java.lang.Object interface:
(defrecord Doodh [id name]
  Object
  (toString [this] (str "#Doodh" (into {} this))))

Because spit uses str, we can now omit the output function and simply invoke spit from e. g. the REPL:
(spit "Doodh.edn" (map->Doodh {:id "134" :name "Berta"}))

Doodh.edn:
      #Doodh{:id 134, :name "Berta"}

Now to make sure that the Doodh will be read back, we invoke clojure.edn/read-string with a custom reader function:
(defn pull [filename]
  (->> (slurp filename)
       (clojure.edn/read-string {:readers {'Doodh map->Doodh}})))

If you read back "Doodh.edn" using the new pull, you should receive a valid Doodh. At the REPL:
(pull "Doodh.edn")
=> #user.Doodh{:id 134, :name "Berta"}

