add LoadSaver for JSON, GOB
diff --git a/cmd/kati/main.go b/cmd/kati/main.go
index 2b1f09f..0b9d30c 100644
--- a/cmd/kati/main.go
+++ b/cmd/kati/main.go
@@ -139,33 +139,38 @@
 	startTime := time.Now()
 
 	if loadGOB != "" {
-		g := kati.LoadDepGraph(loadGOB)
+		g, err := kati.GOB.Load(loadGOB)
 		kati.LogStats("deserialize time: %q", time.Since(startTime))
-		return g, nil
+		return g, err
 	}
 	if loadJSON != "" {
-		g := kati.LoadDepGraphFromJSON(loadJSON)
+		g, err := kati.JSON.Load(loadJSON)
 		kati.LogStats("deserialize time: %q", time.Since(startTime))
-		return g, nil
+		return g, err
 	}
 	return kati.Load(req)
 }
 
-func save(g *kati.DepGraph, targets []string) {
+func save(g *kati.DepGraph, targets []string) error {
+	var err error
 	startTime := time.Now()
 	if saveGOB != "" {
-		kati.DumpDepGraph(g, saveGOB, targets)
+		err = kati.GOB.Save(g, saveGOB, targets)
 		kati.LogStats("serialize time: %q", time.Since(startTime))
 	}
 	if saveJSON != "" {
-		kati.DumpDepGraphAsJSON(g, saveJSON, targets)
+		serr := kati.JSON.Save(g, saveJSON, targets)
 		kati.LogStats("serialize time: %q", time.Since(startTime))
+		if err == nil {
+			err = serr
+		}
 	}
 
 	if useCache && !g.IsCached() {
 		kati.DumpDepGraphCache(g, targets)
 		kati.LogStats("serialize time: %q", time.Since(startTime))
 	}
+	return err
 }
 
 func main() {
@@ -244,7 +249,10 @@
 		kati.LogStats("eager eval command time: %q", time.Since(startTime))
 	}
 
-	save(g, req.Targets)
+	err = save(g, req.Targets)
+	if err != nil {
+		panic(err)
+	}
 
 	if generateNinja {
 		startTime := time.Now()
diff --git a/depgraph.go b/depgraph.go
index cfc3d6a..043fa92 100644
--- a/depgraph.go
+++ b/depgraph.go
@@ -82,7 +82,7 @@
 	}
 
 	if req.UseCache {
-		g := LoadDepGraphCache(req.Makefile, req.Targets)
+		g := loadDepGraphCache(req.Makefile, req.Targets)
 		if g != nil {
 			return g, nil
 		}
@@ -148,3 +148,16 @@
 		exports:     er.exports,
 	}, nil
 }
+
+type Loader interface {
+	Load(string) (*DepGraph, error)
+}
+
+type Saver interface {
+	Save(*DepGraph, string, []string) error
+}
+
+type LoadSaver interface {
+	Loader
+	Saver
+}
diff --git a/serialize.go b/serialize.go
index f97eb8c..cbb10ab 100644
--- a/serialize.go
+++ b/serialize.go
@@ -46,6 +46,17 @@
 	valueTypeTmpval    = 't'
 )
 
+var JSON LoadSaver
+var GOB LoadSaver
+
+func init() {
+	JSON = jsonLoadSaver{}
+	GOB = gobLoadSaver{}
+}
+
+type jsonLoadSaver struct{}
+type gobLoadSaver struct{}
+
 func dumpInt(w io.Writer, i int) {
 	v := int32(i)
 	err := binary.Write(w, binary.LittleEndian, &v)
@@ -240,27 +251,24 @@
 	}
 }
 
-func DumpDepGraphAsJSON(g *DepGraph, filename string, roots []string) {
+func (jsonLoadSaver) Save(g *DepGraph, filename string, roots []string) error {
 	sg := makeSerializableGraph(g, roots)
 	o, err := json.MarshalIndent(sg, " ", " ")
 	if err != nil {
-		panic(err)
+		return err
 	}
-	f, err2 := os.Create(filename)
-	if err2 != nil {
-		panic(err2)
-	}
-	f.Write(o)
-	err = f.Close()
-	if err != nil {
-		panic(err)
-	}
-}
-
-func DumpDepGraph(g *DepGraph, filename string, roots []string) {
 	f, err := os.Create(filename)
 	if err != nil {
-		panic(err)
+		return err
+	}
+	f.Write(o)
+	return f.Close()
+}
+
+func (gobLoadSaver) Save(g *DepGraph, filename string, roots []string) error {
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
 	}
 	e := gob.NewEncoder(f)
 	startTime := time.Now()
@@ -269,10 +277,7 @@
 	startTime = time.Now()
 	e.Encode(sg)
 	LogStats("serialize output time: %q", time.Since(startTime))
-	err = f.Close()
-	if err != nil {
-		panic(err)
-	}
+	return f.Close()
 }
 
 func cacheFilename(mk string, roots []string) string {
@@ -297,7 +302,7 @@
 			return
 		}
 	}
-	DumpDepGraph(g, cacheFile, roots)
+	GOB.Save(g, cacheFile, roots)
 }
 
 func deserializeSingleChild(sv serializableVar) Value {
@@ -564,10 +569,10 @@
 	}
 }
 
-func LoadDepGraphFromJSON(filename string) *DepGraph {
+func (jsonLoadSaver) Load(filename string) (*DepGraph, error) {
 	f, err := os.Open(filename)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
 	defer f.Close()
 
@@ -575,15 +580,16 @@
 	g := serializableGraph{Vars: make(map[string]serializableVar)}
 	err = d.Decode(&g)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
-	return deserializeGraph(g)
+	dg := deserializeGraph(g)
+	return dg, nil
 }
 
-func LoadDepGraph(filename string) *DepGraph {
+func (gobLoadSaver) Load(filename string) (*DepGraph, error) {
 	f, err := os.Open(filename)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
 	defer f.Close()
 
@@ -591,12 +597,13 @@
 	g := serializableGraph{Vars: make(map[string]serializableVar)}
 	err = d.Decode(&g)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
-	return deserializeGraph(g)
+	dg := deserializeGraph(g)
+	return dg, nil
 }
 
-func LoadDepGraphCache(makefile string, roots []string) *DepGraph {
+func loadDepGraphCache(makefile string, roots []string) *DepGraph {
 	startTime := time.Now()
 	defer func() {
 		LogStats("Cache lookup time: %q", time.Since(startTime))
@@ -608,7 +615,11 @@
 		return nil
 	}
 
-	g := LoadDepGraph(filename)
+	g, err := GOB.Load(filename)
+	if err != nil {
+		logAlways("Cache load error: %v", err)
+		return nil
+	}
 	for _, mk := range g.accessedMks {
 		if mk.State != fileExists && mk.State != fileNotExists {
 			panic(fmt.Sprintf("Internal error: broken state: %d", mk.State))