Skip to content

Level 1: CLI Task Tracker (Core Go & Testing)

Welcome to your first Go hands-on challenge! Your goal is to build a Command Line Interface (CLI) application to track tasks, persisted as JSON.

Features to Implement

Your application must support the following CLI commands:

  1. Add a task:
    go run main.go add "Buy groceries"
    # Output: Task added successfully with ID 1
    
  2. List all tasks:
    go run main.go list
    # Output:
    # ID  Description    Status      Created At
    # 1   Buy groceries  [Pending]   2026-07-07 17:15:00
    
  3. Complete a task:
    go run main.go complete 1
    # Output: Task 1 marked as completed
    
  4. Delete a task:
    go run main.go delete 1
    # Output: Task 1 deleted
    

Storage & Persistence

  • All tasks must be stored in a file named tasks.json in the current working directory.
  • If tasks.json does not exist, the app should initialize an empty database.
  • The tasks must be saved dynamically after every add, complete, or delete action.

Python vs Go Cheatsheet for this Project

Here is how you would map typical Python patterns to Go for this project:

1. JSON Serialization

  • Python:
    import json
    # Write
    with open("tasks.json", "w") as f:
        json.dump(tasks, f, indent=4)
    # Read
    with open("tasks.json", "r") as f:
        tasks = json.load(f)
    
  • Go:
    import "encoding/json"
    import "os"
    
    // Write
    data, err := json.MarshalIndent(tasks, "", "  ")
    if err != nil {
        return err
    }
    err = os.WriteFile("tasks.json", data, 0644)
    
    // Read
    data, err := os.ReadFile("tasks.json")
    if err != nil {
        if os.IsNotExist(err) {
            // Handle file not existing (first run)
        }
        return err
    }
    err = json.Unmarshal(data, &tasks)
    

2. Classes vs Structs

  • Python:
    class Task:
        def __init__(self, id, description):
            self.id = id
            self.description = description
            self.done = False
    
  • Go:
    // Fields MUST be capitalized (exported) to be visible to the encoding/json package!
    type Task struct {
        ID          int       `json:"id"`
        Description string    `json:"description"`
        Done        bool      `json:"done"`
        CreatedAt   time.Time `json:"created_at"`
    }
    

3. Error Handling

  • Python:
    def get_task(id):
        if id not in tasks:
            raise ValueError("Task not found")
        return tasks[id]
    
  • Go:
    import "errors"
    
    func (tm *TaskManager) GetTask(id int) (Task, error) {
        task, exists := tm.tasks[id]
        if !exists {
            return Task{}, errors.New("task not found")
        }
        return task, nil
    }
    

Getting Started

  1. Check out task.go. We have defined the skeleton structure and function signatures.
  2. Check out task_test.go, which has standard tests.
  3. Run the tests (they will fail initially because the methods are not implemented):
    /usr/local/go/bin/go test -v ./level1_task_tracker/...
    
  4. Implement the methods in task.go.
  5. Once your tests pass, write main.go to handle the command-line CLI interface.