Thuta Learning
ProjectsProgrammingbeginner

Mini Project: Task Manager (Part 3) - Persistence & Auto-Save

Relax. We'll talk through this in plain words — no textbook voice.

What you'll walk away with

  • Apply Mini Project: Task Manager (Part 3) - Persistence & Auto-Save in a real, hands-on project
  • Write the code yourself and run it
  • Build an entire project step by step

Let's think about this for a second

Part 3 is the final stage of the project, where we'll put the goroutine and channel concepts from the Advanced chapter into practice. Since it's pretty useless if the task list disappears every time the program closes, we'll learn how to save/load it to a tasks.json file using the encoding/json package. On top of that, we'll set up a background goroutine running on a time.Ticker to try out a periodic auto-save pattern. By the end of this stage, the Task Manager project will be a complete mini application combining struct, methods, error handling, file I/O, and goroutines.

Let's build it for real

Write a SaveToFile(filename string) error method that uses json.MarshalIndent to turn the tasks slice into JSON bytes, then uses os.WriteFile to save it to a file. Write a LoadFromFile(filename string) error method that uses os.ReadFile to read the file and json.Unmarshal to fill the tasks slice back in. Start a goroutine that uses time.NewTicker(10 * time.Second) so that every tick from the ticker.C channel automatically calls SaveToFile in the background. Right before the program closes at the end of main(), call SaveToFile one more time to make sure no data is lost.

Code Example

go
package main

import (
	"encoding/json"
	"fmt"
	"os"
	"time"
)

type Task struct {
	ID    int    `json:"id"`
	Title string `json:"title"`
	Done  bool   `json:"done"`
}

type TaskManager struct {
	tasks []Task
}

func (m *TaskManager) SaveToFile(filename string) error {
	data, err := json.MarshalIndent(m.tasks, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(filename, data, 0644)
}

func (m *TaskManager) LoadFromFile(filename string) error {
	data, err := os.ReadFile(filename)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, &m.tasks)
}

func (m *TaskManager) startAutoSave(filename string, interval time.Duration, done chan bool) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			if err := m.SaveToFile(filename); err != nil {
				fmt.Println("auto-save error:", err)
			} else {
				fmt.Println("auto-saved to", filename)
			}
		case <-done:
			return
		}
	}
}

func main() {
	manager := &TaskManager{}
	_ = manager.LoadFromFile("tasks.json")

	manager.tasks = append(manager.tasks, Task{ID: 1, Title: "Finish project"})

	done := make(chan bool)
	go manager.startAutoSave("tasks.json", 10*time.Second, done)

	// program logic runs here...
	time.Sleep(2 * time.Second)

	done <- true
	if err := manager.SaveToFile("tasks.json"); err != nil {
		fmt.Println("final save error:", err)
	}
	fmt.Println("Task Manager project complete!")
}
You should see
The task list gets saved into the tasks.json file as formatted JSON, and the data isn't lost even after the program closes.

Try it in 5 minutes

Within 5 minutes, change the auto-save interval from 10 seconds to 2 seconds, then open the tasks.json file in an editor and observe how its content keeps updating.

One quick word of caution

In a production app, a crash mid-write can corrupt your data, so it's better to use the pattern of writing to a temp file first and then swapping it in atomically with os.Rename.

Easy traps

  • Starting the goroutine with go manager.startAutoSave(...) but never signaling the done channel, so once main() finishes, the goroutine keeps leaking in the background
  • Calling SaveToFile with a bad file path (a directory with no permissions) and ignoring the returned error, so you never actually check whether the data was saved

Now try it yourself

Within 5 minutes, change the auto-save interval from 10 seconds to 2 seconds, then open the tasks.json file in an editor and observe how its content keeps updating.

You'll know it worked when: The task list gets saved into the tasks.json file as formatted JSON, and the data isn't lost even after the program closes.

Mini Project: Task Manager (Part 3) - Persistence & Auto-Save | Thuta Learning