30 Go (Golang) Challenges for Junior to Senior Developers

Sharpen your Go skills with 30 coding challenges tailored for all developer levels. Features concise code examples covering fundamentals, concurrency, and advanced concepts

Go (Golang) Challenges for Every Career Stage

In the world of modern software, Go has carved out a crucial space for building fast, reliable, and highly concurrent systems. For developers, proficiency in Go opens doors to roles in cloud computing, DevOps, and backend services. For employers, finding engineers who can leverage Go’s powerful concurrency model is a top priority.

That’s where focused practice comes in. We’ve designed 30 hands-on Go challenges, split into three distinct levels—10 each for Junior, Mid-Level, and Senior developers. Whether you’re learning to handle goroutines, mastering interfaces, or preparing for a complex system design interview, these challenges will help you level up your Go expertise.

Jump to Your Level

🐹 Junior Developer 🛠️ Mid-Level Developer ⚡️ Senior Developer

Junior Developer Challenges

1. Hello, World!

The traditional first program to verify a working setup.

package main
import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

2. Sum a Slice of Integers

A basic test of loops and slice manipulation.

func SumSlice(numbers []int) int {
    sum := 0
    for _, number := range numbers {
        sum += number
    }
    return sum
}

3. Reverse a String

Demonstrates handling of runes for proper Unicode character reversal.

func ReverseString(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i 

4. Find the Maximum Value in a Slice

A fundamental algorithm using a simple loop.

func FindMax(numbers []int) (int, error) {
    if len(numbers) == 0 {
        return 0, fmt.Errorf("slice is empty")
    }
    max := numbers[0]
    for _, number := range numbers {
        if number > max {
            max = number
        }
    }
    return max, nil
}

5. Check for Palindrome

Tests string manipulation and comparison logic.

func IsPalindrome(s string) bool {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i 

6. FizzBuzz

The classic test of basic conditional logic and loops.

func FizzBuzz(n int) {
    for i := 1; i 

7. Define and Use a Struct

Introduces custom types and data organization.

type Person struct {
    Name string
    Age  int
}

func NewPerson(name string, age int) Person {
    return Person{Name: name, Age: age}
}

8. Basic Error Handling

Shows how to return and check for errors, a core Go concept.

import "strconv"

func ToInt(s string) (int, error) {
    return strconv.Atoi(s)
}
// Usage: if val, err := ToInt("42"); err == nil { ... }

9. Factorial of a Number

A classic introduction to recursion in Go.

func Factorial(n uint) uint {
    if n 

10. Check if a Map Contains a Key

Demonstrates the "comma ok" idiom for map lookups.

func MapContainsKey(m map[string]int, key string) bool {
    _, ok := m[key]
    return ok
}

Mid-Level Developer Challenges

1. Word Frequency Count

Use a map to count the frequency of words in a text.

import "strings"

func WordCount(text string) map[string]int {
    counts := make(map[string]int)
    for _, word := range strings.Fields(text) {
        counts[strings.ToLower(word)]++
    }
    return counts
}

2. Sort a Slice of Structs

Implement the `sort.Interface` to sort custom types.

import "sort"
type Person struct { Name string; Age int }
type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age 

3. Basic Concurrent Data Fetching

Use goroutines and channels to fetch data from multiple sources concurrently.

func FetchData(urls []string) map[string]string {
    ch := make(chan struct{url, body string})
    for _, url := range urls {
        go func(u string) {
            // Assume http.Get(u) returns body
            ch 

4. JSON Marshalling and Unmarshalling

Convert Go structs to and from JSON.

import "encoding/json"
type User struct { ID int `json:"id"`; Name string `json:"name"` }

// Marshal: json.Marshal(User{...})
// Unmarshal: json.Unmarshal(data, &user)

5. Implement a Simple Web Server

Use the `net/http` package to create a server with a basic handler.

import "net/http"
func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, Gopher!")
}

func StartServer() {
    http.HandleFunc("/", helloHandler)
    http.ListenAndServe(":8080", nil)
}

6. Worker Pool with Channels

Create a pool of goroutines to process jobs from a channel.

func WorkerPool(numWorkers int, jobs 

7. Implement a Set Data Structure

Use a map to create a simple Set type with Add, Has, and Remove methods.

type Set map[interface{}]struct{}

func (s Set) Add(v interface{}) { s[v] = struct{}{} }
func (s Set) Has(v interface{}) bool { _, ok := s[v]; return ok }
func (s Set) Remove(v interface{}) { delete(s, v) }

8. Method with Pointer Receiver

Implement a method that modifies the state of a struct.

type Counter struct { count int }

func (c *Counter) Increment() {
    c.count++
}

9. Check if a Binary Tree is a Valid BST

A recursive algorithm that validates the properties of a Binary Search Tree.

type TreeNode struct { Val int; Left, Right *TreeNode }

func IsValidBST(root *TreeNode) bool {
    return isValid(root, nil, nil)
}

func isValid(node, min, max *TreeNode) bool {
    if node == nil { return true }
    if min != nil && node.Val = max.Val { return false }
    return isValid(node.Left, min, node) && isValid(node.Right, node, max)
}

10. Reading and Writing Files

Use the `os` package for basic file I/O operations.

import "os"
// Write
// err := os.WriteFile("test.txt", []byte("Hello"), 0644)
// Read
// data, err := os.ReadFile("test.txt")

Senior Developer Challenges

1. Concurrent Map with Mutex

Build a map that is safe for concurrent read/write operations.

import "sync"
type ConcurrentMap struct {
    mu sync.RWMutex
    items map[string]interface{}
}

func (cm *ConcurrentMap) Set(key string, value interface{}) {
    cm.mu.Lock()
    defer cm.mu.Unlock()
    cm.items[key] = value
}

func (cm *ConcurrentMap) Get(key string) (interface{}, bool) {
    cm.mu.RLock()
    defer cm.mu.RUnlock()
    value, ok := cm.items[key]
    return value, ok
}

2. Graceful Shutdown of an HTTP Server

Use `context` and channels to allow a server to finish active requests before shutting down.

import "os/signal"
// ...
server := &http.Server{Addr: ":8080"}
go func() {
    if err := server.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatalf("ListenAndServe(): %v", err)
    }
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

3. Implement a Rate Limiter

Use a ticker and a channel to limit the rate of events.

func RateLimiter(requests 

4. LRU Cache Implementation

A system design problem requiring a map and a doubly linked list for efficiency.

import "container/list"
type LRUCache struct {
    capacity int
    cache    map[int]*list.Element
    ll       *list.List
}
// ... methods for Get and Put would manipulate the map and linked list ...

5. Context-Aware Function

Write a function that accepts a `context.Context` and can be cancelled.

func LongRunningTask(ctx context.Context) error {
    for {
        select {
        case 

6. Use Reflection to Inspect a Struct

Use the `reflect` package to dynamically read struct tags and values.

import "reflect"
func InspectStruct(s interface{}) {
    val := reflect.ValueOf(s).Elem()
    for i := 0; i 

7. Build a Simple HTTP Middleware

Create a logging middleware for an `http.Handler`.

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Request: %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}
// Usage: http.Handle("/", LoggingMiddleware(myHandler))

8. Fan-in, Fan-out Concurrency Pattern

A pattern for distributing work and then merging the results.

func FanIn(inputs ...

9. Solve the Dining Philosophers Problem

A classic concurrency problem to demonstrate deadlock avoidance.

// The solution involves giving each philosopher two forks (mutexes)
// and ensuring they pick them up in a consistent order (e.g., lowest
// index first) to prevent circular waits and deadlock.
// The implementation requires a struct for philosophers, a slice
// of mutexes for forks, and a main loop for eating/thinking.

10. Implement a Circuit Breaker

A design pattern to prevent repeated calls to a failing external service.

// The implementation would involve a struct that tracks the state
// (Closed, Open, Half-Open), failure counts, and timers. A method
// like Execute(fn) would wrap the external call, checking the state
// before proceeding and updating counts on success or failure.

Tips to Prepare for Go Coding Challenges

  • Think Concurrently: Many Go problems have a concurrent angle. Always consider if goroutines and channels can provide a more efficient or elegant solution.
  • Handle Errors Explicitly: Don't ignore errors. Demonstrating proper `if err != nil` checks is a sign of a mature Go developer. Avoid panicking unless absolutely necessary.
  • Know the Standard Library: Go has a powerful standard library. Get familiar with packages like `net/http`, `encoding/json`, `sync`, `context`, and `sort`.
  • Understand Slices and Maps: Be clear on the difference between a slice's length and capacity, and know how maps work internally (e.g., they are not safe for concurrent use by default).
  • Use `go fmt`: Always format your code. It's a non-negotiable part of the Go ecosystem and shows you care about readability and convention.
  • Embrace Interfaces: Understand how interfaces provide decoupling. Be ready to explain why you might accept an `io.Reader` instead of a concrete `*os.File`.

Conclusion

Go's simplicity and power make it a fantastic language for modern software development. Mastering it comes from practice and a deep understanding of its core philosophies, especially concurrency and error handling. By tackling these challenges, you'll build the skills and confidence needed to excel in your Go journey. Happy coding!

Remote hiring made easy

75%
faster to hire
58%
cost savings
2K+
hires made

Our Offices

415 Mission St, San Francisco,
CA 94105, United States

320 Serangoon Road #13-05, Centrium Square, Singapore 218108

6/F, SAVISTA Realty Building, Binh Thanh, Ho Chi Minh City, Vietnam

12/F, Honest Building, 09-11 Leighton Rd, Causeway Bay, Hong Kong

Nongsa Digital Park, Jalan Hang Lekiu, Kota Batam, Provinsi Kepulaunan Riau, 29466

Level 16, Menara Etiqa, No. 3, Jalan Bangsar Utama 1, 59000, Kuala Lumpur, Malaysia

2/F, JKSA Building, 4954-A A. Arnaiz Ave. cor. Mayor St., Pio del Pilar, Makati City, Philippines