Technology / Programming

Golang Tutorial: How to Write Scalable Go Code

Want to learn Go?  In this GoLang tutorial, learn how to leverage implicit Goroutines to create a function that takes server requests. Continue Reading
Follow us
Published on August 29, 2023

Google created Golang code with scalability in mind. Back in 2009, Google servers were taking billions of search requests, with web crawlers having to analyze millions of websites. Their growth was unprecedented, and only projected to increase. They needed a solution that would have the ability to scale along with the incredible workloads they were processing from search requests.

Enter Go (also known as Golang), Google's own open-source programming language. Officially introduced in 2012, the Go programming language is now in its 20th release at the time of this writing. Fans of Go will tell you it's easy to learn, with a simple syntax and well-designed standard library for common network service tasks that make it a reliable, easy-to-use language.

In this Golang tutorial, you'll learn various ways of creating scalable Go code. But what exactly does it mean for Go code to be scalable? Let's dive in. 

What is Scalability?

Generally speaking, scalability refers to a system’s ability to handle large increases in workload without sacrificing usability or functionality. It is vital when creating a software application that will see workloads over the internet. Go has the ability to efficiently handle scalability.

RELATED: Should You Use Golang or Java for Your Next Project?

How is Golang Scalable?

Go was created from the ground up to scale. This is done primarily through concurrency and Goroutines. Goroutines are a fundamental feature of Go, used for concurrent programming. Goroutines were written to optimize memory and have a very low overhead. This allows programmers to run potentially millions of Goroutines at once. 

If you want to learn Go, let's begin with a look at a basic function that uses concurrency.

Golang Tutorial: Creating a Function that Takes Server Requests

This example will leverage implicit Goroutines to create a function that takes server requests. Server requests are the perfect example of concurrent programming because they receive thousands of login requests and more from users simultaneously. Below is a straightforward web server written in Go. 

In short, this function starts up a server using the “net/http” Go library and then returns the string “Request Handled!” back to the client. Let’s go over each line at a time for a deeper understanding.

package main
import (
    "fmt"
    "net/http"
)

func main() {
    // Register the request handler
    http.HandleFunc("/", handleRequest)

    // Start the web server on port 8080
    if err := http.ListenAndServe(":8080", nil); err != nil {
        fmt.Println("Error starting the server:", err)
    }
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
    // Perform some processing for each request
    doLoginValidation(r)

    // Send a response to the client
    fmt.Fprint(w, “Request Handled!”)
}

In the first couple of lines, we just declare the package the code is located in and import some packages we need. That is pretty much the same in every programming language

Next, we declare a function called “main.” When the code is run, this function will be called to fire up the server. 

The first line of code within the main class is the most important part of the code. http.HandleFunc takes a context path and function to handle a particular request. In this instance, handleRequest is passed in as a callback function and is activated whenever the server receives a request from a client. 

The next line of code starts with the web server on port 8080. However, this is done in a defensive manner. Any error starting the server is called out and printed directly to the user.

You may have read all the code and wondered where the concurrency is actually happening. The method http.HandlFunc is actually a threaded event, even though a Goroutine is not explicitly called. That's because the library HandleFunc handles the Goroutines internally. This is one of the great things about Go: many common tasks are concurrent by default, saving time and troubleshooting.

But what if you want something to explicitly be a Goroutine? Let’s take a look at another code example. 

package main
import (
    "fmt"
    “sync"
)
func main() {
    numbers := []int{1, 2, 3, 4, 5}
    // Create a wait group to synchronize Goroutines
    var wg sync.WaitGroup

    // Set the wait group counter
    wg.Add(1)
    // Process each number concurrently
    for _, num := range numbers {
        go getSquare(num, &wg)
    }
    // Wait for all Goroutines to complete
    wg.Wait()
    fmt.Println("All Goroutines have completed.")
}
func getSquare(number int, wg *sync.WaitGroup) {
    defer wg.Done()
    square := number * number
    fmt.Printf("Square of %d is %d\n", number, square)
}

In the example above, we are calculating the square root of each number in the array labeled “numbers.” The logic itself is fairly trivial, so spending time on it is not much use.

However, take note that the integers are all calculated concurrently. The example uses a few Golang code keywords related to concurrency that are worth calling out.

Go Code Keywords Related to Concurrency

In the above example, we used the following Go code keywords to implement concurrency:

  • Sync.waitgroup. This provides a simple way of coordinating multiple synchronous calls. Putting wg.Add(len(numbers)) generally signifies a concurrent operation is about to begin. 

  • Go. Go is the keyword to actually invoke a Goroutine. Adding this keyword before a method makes that method concurrent. 

  • wg.Wait(). This will block the main routine until the wg.Add() is decremented down to zero. Wg.Add is decremented every time wg.Done() is called. So, wg.Wait() essentially means, “Do not continue the function until all concurrent operations are complete.” 

  • Defer. Defer is a powerful feature in Go that allows you to execute a function when the surrounding function exits. This is a great way to mitigate memory leaks and ensure code is executed at the moment you want it to be. 

Ready to Learn More Golang?

This Golang tutorial offered insight into some Go code basics. But this is only scratching the surface — there's so much more you can learn! CBT Nuggets programming and development training can help you learn more about Go and a number of other programming languages like Java, ReactJS, PowerShell, Python, and more. Once you get a few hours of training and practice under your belt, you'll be on your way to thinking like a programmer and writing code that'll improve your process (and your life).

Not a CBT Nuggets subscriber? Sign up for a 7-day free trial.


Ultimate DevOps Cert GuideUltimate DevOps Cert Guide

By submitting this form you agree to receive marketing emails from CBT Nuggets and that you have read, understood and are able to consent to our privacy policy.


Don't miss out!Get great content
delivered to your inbox.

By submitting this form you agree to receive marketing emails from CBT Nuggets and that you have read, understood and are able to consent to our privacy policy.

Get CBT Nuggets IT training news and resources

I have read and understood the privacy policy and am able to consent to it.

© 2025 CBT Nuggets. All rights reserved.Terms | Privacy Policy | Accessibility | Sitemap | 2850 Crescent Avenue, Eugene, OR 97408 | 541-284-5522