Go Programming — Block 2: Store Data, Organize Code, Build Real Programs

Go Programming — Block 2: Files, Data, and Project Structure

Chapters 11–20 · Beginner-friendly · Practical examples · Complete answers included

Block 1 taught you the foundations: variables, conditions, loops, functions, slices, maps, strings, and basic debugging.

Block 2 teaches you to build programs that organize information, save data, read files, and use multiple source files.

Since you will publish this material in a Blogspot post, every MCQ, practical exercise, and challenge will include its answer or a complete working solution. Nothing will depend on an instructor being present to explain the missing steps.

What you will learn

11. Pointers — only the practical basics

Understand how a function can change a variable belonging to its caller.

12. Structs and custom data types

Group a product's name, price, and stock into one record.

13. Methods and interfaces

Attach actions to your own data types and understand how different types can satisfy the same behavior.

14. Packages and modules

Organize code across files and folders without making one enormous source file.

15. Reading and writing files

Save information and load it again after a program closes.

16. JSON and data conversion

Exchange structured data with websites, APIs, and other programs.

17. Error handling

Detect problems such as missing files and invalid data instead of letting them go unnoticed.

18. Dates, times, and standard libraries

Work with dates, timestamps, formatting, and useful built-in tools.

19. Testing your code

Automatically check whether your functions return the correct results.

20. Project structure and Git basics

Arrange a real project, track code changes, and recover earlier versions.

Chapter 11 — Pointers: changing information through a function

A pointer sounds complicated, but the basic idea is simple.

Imagine that price is a box containing 100. A pointer gives a function a way to access that same box, rather than just receiving a separate copy of the number.

MCQ 1

What happens when you pass a normal integer variable to a function in Go?

  • A. The function automatically receives a pointer.

  • B. The function receives a copy of the integer value.

  • C. The original variable is deleted.

  • D. Go refuses to pass integers to functions.

Correct answer: B.

Go passes the value. For an integer, the function receives a copy, so changing its local parameter does not change the caller's variable.

MCQ 2

What will this program print?

Go

package main

import "fmt"

func changeValue(number *int) {
    *number = 50
}

func main() {
    price := 100
    changeValue(&price)
    fmt.Println(price)
}
  • A. 100

  • B. 50

  • C. 0

  • D. A compilation error

Correct answer: B — 50.

Why?

  • &price obtains a pointer to price.

  • number *int means that number is a pointer to an integer.

  • *number = 50 changes the value at the location that the pointer refers to.

  • Therefore, the original price becomes 50.

Practical task — Increase a product's price

Save as main.go.

Go

package main

import "fmt"

func increasePrice(price *float64) {
    *price = *price + 10
}

func main() {
    price := 100.0

    increasePrice(&price)

    fmt.Println("Updated price:", price)
}

Output:

Updated price: 110

Real-life use: A function may need to update a product's stock, change an account setting, or modify a record supplied by the calling code.

Remember: You do not need pointers everywhere. Use them when you need to modify a value through a pointer or when the design calls for sharing access to data. Go still passes pointer values by value.

Chapter 12 — Structs and custom data types

A struct groups related pieces of information into one record.

Imagine you have a product. Instead of maintaining separate lists for product names, prices, and stock quantities, you can keep the information for each product together.

MCQ 3

Which Go feature is designed to group related fields such as a product's name, price, and quantity?

  • A. Struct

  • B. Loop

  • C. Comment

  • D. Import statement

Correct answer: A — Struct.

MCQ 4

What will this code print?

Go

type Product struct {
    Name  string
    Price float64
}

func main() {
    item := Product{Name: "Book", Price: 50}
    fmt.Println(item.Name)
}
  • A. Product

  • B. 50

  • C. Book

  • D. Nothing

Correct answer: C — Book.

item.Name accesses the Name field of the product record.

Practical task — Create a product record

Folder structure:

product-app/
└── main.go

Code:

Go

package main

import "fmt"

type Product struct {
    Name     string
    Price    float64
    Quantity int
}

func main() {
    item := Product{
        Name:     "Notebook",
        Price:    40,
        Quantity: 5,
    }

    total := item.Price * float64(item.Quantity)

    fmt.Println("Product:", item.Name)
    fmt.Println("Stock:", item.Quantity)
    fmt.Println("Stock value:", total)
}

Output:

Product: Notebook
Stock: 5
Stock value: 200

Line-by-line concepts

  • type Product struct defines a new data structure.

  • Name, Price, and Quantity are fields.

  • Product{...} creates a value containing those fields.

  • item.Price accesses the price.

  • float64(item.Quantity) converts the quantity to a decimal-compatible numeric type.

  • Multiplying the price by the quantity calculates the total stock value.

Real-life use: Product catalogs, student records, customer details, employee information, and order records.

Chapter 13 — Methods and interfaces

A method is a function associated with a particular type.

An interface describes behavior: for example, any type that can provide a Total() method returning a float64 can satisfy an interface that requires that method.

MCQ 5

What is the primary purpose of a method?

  • A. To associate an operation with a type.

  • B. To create a new folder.

  • C. To install Go.

  • D. To replace all variables.

Correct answer: A.

MCQ 6

Consider this code:

Go

type Product struct {
    Price    float64
    Quantity int
}

func (p Product) Total() float64 {
    return p.Price * float64(p.Quantity)
}

How can you calculate the total for a product stored in item?

  • A. item.Total()

  • B. Product.Total

  • C. item.Price()

  • D. Total.Product()

Correct answer: A — item.Total().

Practical task — Give a product its own calculation method

Go

package main

import "fmt"

type Product struct {
    Name     string
    Price    float64
    Quantity int
}

func (p Product) Total() float64 {
    return p.Price * float64(p.Quantity)
}

func main() {
    item := Product{
        Name:     "Pen",
        Price:    10,
        Quantity: 8,
    }

    fmt.Println("Product:", item.Name)
    fmt.Println("Total:", item.Total())
}

Output:

Product: Pen
Total: 80

The expression func (p Product) Total() defines a method on Product. The variable p represents the product being used.

A small interface example

Go

type Totaler interface {
    Total() float64
}

Any type with a matching Total() float64 method satisfies this interface automatically. You don't have to explicitly declare that it implements the interface.

When do you need interfaces? When different kinds of objects need to be used through the same behavior. Learn the basic idea now; advanced interface design can wait until you build larger applications.

Chapter 14 — Packages, modules, and multiple files

You do not have to put your entire program into one file. Go lets you organize code into packages and directories.

A package groups related Go source files. A module identifies a Go project and its dependencies.

MCQ 7

What is the main purpose of a Go module?

  • A. To identify a Go project and manage its dependencies.

  • B. To print messages.

  • C. To replace every function.

  • D. To make all code public automatically.

Correct answer: A.

MCQ 8

If two .go files are in the same directory and both declare package main, can they normally be compiled together as one executable?

  • A. No, Go only allows one source file per program.

  • B. Yes, provided their declarations do not conflict.

  • C. Only if both files have the same filename.

  • D. Only if neither file contains functions.

Correct answer: B.

Practical task — Split a program into two files

Folder structure:

greeting-app/
├── go.mod
├── main.go
└── greeting.go

Create the module from inside greeting-app:

Bash

go mod init example.com/greeting-app

The command creates go.mod. You do not need to type the generated module file manually.

main.go:

Go

package main

import "fmt"

func main() {
    message := greeting("Pramod")
    fmt.Println(message)
}

greeting.go:

Go

package main

func greeting(name string) string {
    return "Hello, " + name
}

Run the project from the folder containing go.mod:

Bash

go run .

Output:

Hello, Pramod

Important: Both files belong to package main, so the function in greeting.go is available to main.go. When compiling a package, Go compiles the relevant source files together.

When should you create a separate package?

Create separate packages when code forms a useful, reusable group with a clear responsibility. Merely splitting every function into a different folder creates unnecessary complexity.

Chapter 15 — Reading and writing files

So far, information stored in variables disappears when the program ends. Files allow a program to save information for later.

MCQ 9

What is the main advantage of saving information to a file?

  • A. The information can be read again after the program closes.

  • B. The program no longer needs variables.

  • C. All errors disappear.

  • D. The file automatically becomes a database.

Correct answer: A.

MCQ 10

Which standard-library package provides common file-reading and file-writing functions?

  • A. fmt

  • B. os

  • C. math

  • D. strings

Correct answer: B — os. The os package provides functions such as ReadFile and WriteFile.

Practical task — Save and read a note

Folder structure:

file-app/
└── main.go

Code:

Go

package main

import (
    "fmt"
    "os"
)

func main() {
    err := os.WriteFile(
        "note.txt",
        []byte("Learn Go every day."),
        0644,
    )
    if err != nil {
        fmt.Println("Could not save the note:", err)
        return
    }

    data, err := os.ReadFile("note.txt")
    if err != nil {
        fmt.Println("Could not read the note:", err)
        return
    }

    fmt.Println(string(data))
}

Run:

Bash

go run main.go

Output:

Learn Go every day.

The program also creates note.txt in its working directory.

Understand the important lines:

  • os.WriteFile writes data to a file. If the file does not exist, it creates it.

  • []byte(...) converts text into bytes, which are the units used to represent file data.

  • 0644 sets the file's permission bits on systems that support those permissions.

  • err != nil checks whether the operation returned an error.

  • os.ReadFile loads the file's contents into memory.

  • string(data) converts the bytes into text.

Real-life use: Saving reports, exporting results, storing configuration, and writing simple logs.

For a simple example, this is enough. For multiple users, concurrent writes, or important business data, you need stronger data-management techniques.

Chapter 16 — JSON and data conversion

JSON is a common format for exchanging structured information between applications. It can represent objects, arrays, strings, numbers, Booleans, and null values.

For example:

JSON

{
  "name": "Notebook",
  "price": 40,
  "quantity": 5
}

MCQ 11

What is JSON commonly used for?

  • A. Exchanging structured data between applications.

  • B. Increasing the computer's physical memory.

  • C. Replacing all programming languages.

  • D. Creating hardware.

Correct answer: A.

MCQ 12

Which Go package provides JSON encoding and decoding?

  • A. encoding/json

  • B. fmt/json

  • C. os/json

  • D. strings/json

Correct answer: A — encoding/json.

Practical task — Convert a product into JSON

Go

package main

import (
    "encoding/json"
    "fmt"
)

type Product struct {
    Name     string  `json:"name"`
    Price    float64 `json:"price"`
    Quantity int     `json:"quantity"`
}

func main() {
    item := Product{
        Name:     "Notebook",
        Price:    40,
        Quantity: 5,
    }

    data, err := json.MarshalIndent(item, "", "  ")
    if err != nil {
        fmt.Println("Could not create JSON:", err)
        return
    }

    fmt.Println(string(data))
}

Output:

JSON

{
  "name": "Notebook",
  "price": 40,
  "quantity": 5
}

What matters here?

  • encoding/json contains Go's standard JSON tools.

  • json.MarshalIndent converts a Go value into readable JSON bytes.

  • The tags, such as json:"name", specify the JSON field names.

  • string(data) converts the generated bytes into printable text.

  • The error check handles a failed conversion.

Practical extension — Decode JSON

To turn JSON back into a Go value, use json.Unmarshal.

Go

var item Product

err := json.Unmarshal(data, &item)
if err != nil {
    fmt.Println("Invalid JSON:", err)
    return
}

Here, data must contain valid JSON bytes, and item receives the decoded product. The &item pointer allows the function to fill in the struct's fields.

Real-life use: API responses, application settings, exported data, and communication between a website and a Go backend.

Chapter 17 — Error handling

An error is information that tells you an operation failed. Go functions commonly return an error value so your program can decide what to do next.

MCQ 13

What does this condition check?

Go

if err != nil {
    fmt.Println("Something went wrong")
}
  • A. Whether the program has finished successfully.

  • B. Whether an error value exists.

  • C. Whether a variable is a string.

  • D. Whether a loop should start.

Correct answer: B. In Go, nil represents the absence of a value for certain types, including the built-in error interface. If err != nil, an error was reported.

MCQ 14

A program tries to read a file that does not exist. What should a reliable program generally do?

  • A. Pretend the file was read successfully.

  • B. Check the returned error and respond appropriately.

  • C. Ignore all errors.

  • D. Delete every other file.

Correct answer: B.

Practical task — Handle a missing file

Go

package main

import (
    "fmt"
    "os"
)

func main() {
    data, err := os.ReadFile("missing.txt")

    if err != nil {
        fmt.Println("File could not be read.")
        return
    }

    fmt.Println(string(data))
}

If missing.txt does not exist in the working directory, the program prints:

File could not be read.

Why not ignore the error? If you try to process the file's contents without checking whether the read succeeded, your program may produce confusing results or fail later.

Practical rule: Whenever a Go function returns an error, understand what the function promises and handle the error appropriately. Do not automatically ignore it.

Chapter 18 — Dates, times, and useful standard libraries

Go includes many standard-library packages, so you can solve common tasks without installing extra dependencies.

The time package handles dates, times, durations, and formatting.

MCQ 15

Which package is commonly used to work with dates and times in Go?

  • A. time

  • B. fmt

  • C. os

  • D. strings

Correct answer: A — time.

MCQ 16

What does time.Now() return?

  • A. The current time as a time.Time value.

  • B. The number of files on your computer.

  • C. A random number.

  • D. A formatted JSON document.

Correct answer: A.

Practical task — Display the current date and time

Go

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()

    fmt.Println("Current date and time:", now.Format("2006-01-02 15:04:05"))
    fmt.Println("Year:", now.Year())
    fmt.Println("Month:", now.Month())
}

Example output:

Current date and time: 2026-10-03 10:30:45
Year: 2026
Month: October

The example output is illustrative; your actual date and time will depend on when and where you run the program.

A special Go rule: The layout "2006-01-02 15:04:05" is Go's reference layout for formatting dates and times. You use those reference numbers to indicate the desired format.

Real-life use: Recording when an order was placed, creating timestamps for logs, calculating durations, and displaying dates in reports.

Other useful standard-library packages include:

  • math — mathematical functions.

  • strconv — converting strings and numeric values.

  • path/filepath — working with file paths.

  • sort — sorting values.

  • net/http — building HTTP clients and servers.

Learn a package when a practical task requires it rather than memorizing the entire standard library.

Chapter 19 — Testing your code

Testing means checking that a program produces the expected result. Automated tests let you repeat those checks whenever you change your code.

MCQ 17

What is the purpose of a unit test?

  • A. To check a small piece of code independently.

  • B. To install an operating system.

  • C. To replace all comments.

  • D. To guarantee that a program has no possible bugs.

Correct answer: A. A unit test checks a function or another small unit of behavior. Passing tests do not prove that every possible bug has been eliminated.

MCQ 18

What command runs Go tests in the current module and its subpackages?

  • A. go start

  • B. go test ./...

  • C. go delete

  • D. go install all-tests

Correct answer: B — go test ./....

Practical task — Write your first automated test

Folder structure:

math-app/
├── go.mod
├── math.go
└── math_test.go

Initialize the project:

Bash

go mod init example.com/math-app

math.go:

Go

package main

func Add(a int, b int) int {
    return a + b
}

math_test.go:

Go

package main

import "testing"

func TestAdd(t *testing.T) {
    result := Add(3, 4)

    if result != 7 {
        t.Errorf("Add(3, 4) = %d; want 7", result)
    }
}

Run the test from the project directory:

Bash

go test

Expected output will include:

PASS
ok

The exact output also includes the package path and execution time.

How it works:

  • Test files normally end in _test.go.

  • Test functions begin with Test and accept *testing.T.

  • Add(3, 4) calls the function.

  • The if condition compares the actual result with the expected result.

  • t.Errorf(...) reports a failure if the values do not match.

Real-life use: After modifying a discount calculation, you can run the tests again to check that the calculation still works.

Chapter 20 — Project structure and Git basics

As programs grow, you need two forms of organization:

  1. Project structure: Keep related code in sensible files and folders.

  2. Version control: Track changes so you can understand what changed and return to an earlier saved version when needed.

Git is a tool for tracking changes to files. GitHub is a hosting service where you can store and share Git repositories.

MCQ 19

What is the main purpose of Git?

  • A. Track changes to files and versions of a project.

  • B. Execute Go code automatically without a compiler.

  • C. Replace the Go programming language.

  • D. Create databases automatically.

Correct answer: A.

MCQ 20

What does git status show?

  • A. The current state of the working directory and staging area.

  • B. The current price of a product.

  • C. The contents of every database on your computer.

  • D. The result of every Go test ever written.

Correct answer: A. It helps you see which files are modified, untracked, or staged for a commit.

Practical task — Track a Go project using Git

Start with:

my-go-project/
├── go.mod
└── main.go

From inside the project directory, initialize Git:

Bash

git init

Create a .gitignore file if needed:

*.exe
*.test
.env

This example ignores Windows executable files, Go test binaries, and a common environment-file name. Add other generated files or local secrets as appropriate for your project.

Check the repository:

Bash

git status

Stage the files you want to track:

Bash

git add go.mod main.go .gitignore

Save a snapshot of the staged changes:

Bash

git commit -m "Initial Go project"

If Git asks you to configure your name and email, configure them for your own commits before retrying.

What the commands mean:

  • git init creates a local Git repository.

  • git status shows the state of the repository.

  • git add stages changes for the next commit.

  • git commit records the staged snapshot in the project's history.

Real-life use: You can experiment with a feature, compare changes, review what you modified, and collaborate with other developers. Git does not automatically upload your project to GitHub; publishing a remote repository requires additional setup.

Block 2 — Combined Practical Project

Build a product inventory manager

You will now combine structs, methods, slices, JSON, files, error handling, dates, and tests in one small project.

The application will:

  • Store product information.

  • Calculate stock value.

  • Save product data to a JSON file.

  • Load saved information when needed.

  • Display a timestamp.

  • Include an automated test.

This is a local learning project, not a production inventory system.

1. Folder structure

Create the following files:

inventory-app/
├── go.mod
├── main.go
├── inventory.go
└── inventory_test.go

Initialize the module inside inventory-app:

Bash

go mod init example.com/inventory-app

2. File: inventory.go

This file defines the product structure and its calculations, along with the functions that save and load inventory data.

Go

package main

import (
    "encoding/json"
    "os"
)

type Product struct {
    Name     string  `json:"name"`
    Price    float64 `json:"price"`
    Quantity int     `json:"quantity"`
}

func (p Product) StockValue() float64 {
    return p.Price * float64(p.Quantity)
}

func SaveInventory(filename string, products []Product) error {
    data, err := json.MarshalIndent(products, "", "  ")
    if err != nil {
        return err
    }

    return os.WriteFile(filename, data, 0644)
}

func LoadInventory(filename string) ([]Product, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return nil, err
    }

    var products []Product

    err = json.Unmarshal(data, &products)
    if err != nil {
        return nil, err
    }

    return products, nil
}

Understand the important parts

  • Product groups the name, price, and quantity.

  • StockValue() calculates the total value of a product's stock.

  • SaveInventory() converts the slice into JSON and writes it to a file.

  • LoadInventory() reads the file and converts the JSON back into a slice.

  • error is returned to the caller when an operation fails.

  • return nil, err indicates that no usable product slice is being returned alongside the error.

3. File: main.go

Go

package main

import (
    "fmt"
    "os"
    "time"
)

func main() {
    filename := "inventory.json"

    products := []Product{
        {Name: "Notebook", Price: 40, Quantity: 20},
        {Name: "Pen", Price: 10, Quantity: 50},
        {Name: "Bag", Price: 500, Quantity: 5},
    }

    if err := SaveInventory(filename, products); err != nil {
        fmt.Println("Save failed:", err)
        return
    }

    savedProducts, err := LoadInventory(filename)
    if err != nil {
        fmt.Println("Load failed:", err)
        return
    }

    fmt.Println("INVENTORY REPORT")
    fmt.Println("Generated:", time.Now().Format("2006-01-02 15:04:05"))

    totalValue := 0.0

    for _, product := range savedProducts {
        value := product.StockValue()
        totalValue += value

        fmt.Printf(
            "%s | Price: %.2f | Quantity: %d | Value: %.2f\n",
            product.Name,
            product.Price,
            product.Quantity,
            value,
        )
    }

    fmt.Printf("Total inventory value: %.2f\n", totalValue)

    if _, err := os.Stat(filename); err == nil {
        fmt.Println("Inventory file saved successfully.")
    }
}

4. File: inventory_test.go

Go

package main

import "testing"

func TestStockValue(t *testing.T) {
    product := Product{
        Name:     "Notebook",
        Price:    40,
        Quantity: 5,
    }

    got := product.StockValue()
    want := 200.0

    if got != want {
        t.Errorf("StockValue() = %v; want %v", got, want)
    }
}

5. Run the application

From the project directory:

Bash

go run .

Expected output, with the timestamp varying according to your computer's clock:

INVENTORY REPORT
Generated: 2026-10-03 10:30:45
Notebook | Price: 40.00 | Quantity: 20 | Value: 800.00
Pen | Price: 10.00 | Quantity: 50 | Value: 500.00
Bag | Price: 500.00 | Quantity: 5 | Value: 2500.00
Total inventory value: 3800.00
Inventory file saved successfully.

The program creates inventory.json in the working directory. Open it to see the saved product records in JSON format.

Run the automated test:

Bash

go test ./...

The test checks that a product priced at 40 with a quantity of 5 has a stock value of 200.

Important limitation: This introductory application recreates and saves the sample inventory each time it runs. It demonstrates saving and loading, but it does not yet preserve user edits or provide an interactive inventory menu. Those are natural extensions for later projects.

Final Block 2 Skill Test

Try these questions before opening the answer key.

10 MCQs

0/10 answered

1. What does `&price` give you?

A. A copy of the price as a string

B. A pointer to the price variable

C. A JSON document

D. A new file

2. What is a struct used for?

A. Repeating a task

B. Grouping related fields

C. Installing packages

D. Formatting dates

3. Which statement about Go interfaces is correct?

A. Every type must declare implements explicitly

B. An interface describes required methods

C. Interfaces are only for printing

D. Interfaces replace structs

4. What does `go run .` do inside a Go module directory?

A. Runs the package in the current directory

B. Deletes the module

C. Runs all tests only

D. Uploads the project to GitHub

5. What does `os.ReadFile` return?

A. Only a Boolean

B. File contents and an error

C. A database connection

D. A timestamp

6. Which package handles JSON conversion?

A. encoding/json

B. time/json

C. fmt/json

D. os/json

7. What should you normally do if a file operation returns a non-nil error?

A. Always ignore it

B. Check and handle the failure

C. Assume the file exists

D. Convert the error to a date

8. What does `time.Now()` provide?

A. The current date and time

B. The current Git commit

C. A random duration

D. The number of files

9. What is the purpose of a Go test?

A. Check expected behavior automatically

B. Replace every application function

C. Guarantee zero bugs

D. Install third-party software

10. What does `git commit` do?

A. Records staged changes in repository history

B. Runs a Go application

C. Reads a JSON file

D. Creates a struct

Check all answers

Final practical challenges — with solutions

Challenge 1: Update a struct

Create a Product struct with a name and price. Change the price from 100 to 125.

Show complete solution

Challenge 2: Save data

Save the text Go is useful to message.txt, then read and print it.

Show complete solution

Challenge 3: Test a calculation

Write a function that doubles an integer and create a test that verifies double(5) equals 10.

Show complete solution

What you should be able to do after Block 2

  • Group information using structs and attach useful methods.

  • Understand basic pointers and interfaces.

  • Split a Go application across source files and initialize a module.

  • Save and load information using files and JSON.

  • Check errors instead of ignoring failed operations.

  • Work with dates and standard-library tools.

  • Write basic automated tests.

  • Use Git to track changes to a project.

You do not need to master every advanced feature of pointers, interfaces, or package design yet. The aim is to use these building blocks confidently.

Next: Block 3 moves into command-line applications, databases, SQL, HTTP, web servers, REST APIs, forms, authentication, and deployment — the skills that begin turning your Go knowledge into usable applications.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.