taking longer than expected. You will find similar examples throughout, notably when sending or receiving network requests in Chapters 4, 7, 10, and 11. We will refer to these techniques as introducing robustness in our applications.
In Chapter 10, you will learn to handle transient failures in your gRPC client applications. You will write your applications in a manner in which they can tolerate temporary failures that are likely to be resolved soon. We refer to this as introducing resilient behavior in our applications. However, we also introduce an upper time limit, which we allow to resolve the potentially temporary failure. If this time limit is exceeded, we consider that the operation cannot be completed. Thus, we introduce robustness as well.
To summarize, resiliency and robustness both aim to handle unexpected situations in our applications, and this book uses these terms to refer to such techniques.
Production Readiness
I use the term production readiness in the book as all steps that you should think about as you develop your application but before you deploy it to any kind of a production environment. When the production environment is your own personal server where you are the only user of your application, the techniques that you will learn will likely be sufficient. If the production environment means that your application will perform critical functionality for your users, then the techniques in this book should be the absolute baseline and a starting point. Production readiness consists of a vast body of often domain-specific techniques across various dimensions—robustness and resiliency, observability, and security. This book shows you how to implement a small subset of these topics.
Reference Documentation
The code listings in the book use various standard library packages and a few third-party packages. The descriptions of the various functions and types are limited to the contextual usage. Knowing where to look when you want to find out more about a package or function is important to get the most out of the book. The key reference documentation for all standard library packages is https://pkg.go.dev/std
. When I import a package as net/http
, the documentation for that package will be found at the path https://pkg.go.dev/net/http
. When I refer to a function such as io.ReadAll()
, the function reference is the package io
's documentation at https://pkg.go.dev/io
.
For third-party packages, the documentation is available by going to the address https://pkg.go.dev/<import path>
. For example, the Go gRPC package is imported as google.golang.grpc
. Its reference documentation is available at https://pkg.go.dev/google.golang.org/grpc
.
Go Refresher
I recommend going through the topics in “A Tour of Go,” at https://tour.golang.org/list
, to serve as a refresher of the various features that we will be using to implement programs in the book. These include for loops, functions, methods, struct and interface types, and error values. Additionally, I want to highlight the key topics that we will use extensively, along with references to learn more about them.
Struct Type
We will be using struct types defined by the standard library and third-party packages, and we will also be defining our own. Beyond defining objects of struct types, we will be working with types that embed other types—other struct types and interfaces. The section “Embedding” in the “Effective Go” guide (https://golang.org/doc/effective_go#embedding
) describes this concept. We will also be making use of anonymous struct types when writing tests. This is described in this talk by Andrew Gerrand, “10 things you (probably) don't know about Go”: https://talks.golang.org/2012/10things.slide#1
.
Interface Type
To use the various library functions and to write testable applications, we will be making extensive use of interface types. For example, we will be making extensive use of alternative types that satisfies the io.Reader
and io.Writer
interfaces to write tests for applications that interface with the standard input and output.
Learning to define a custom type that satisfies another interface is a key step to writing Go applications, where we plug in our functionality to work with the rest of the language. For example, to enable sharing data across HTTP handler functions, we will define our own custom type implementing the http.Handler
interface.
The section on interfaces in “A Tour of Go,” https://tour.golang.org/methods/9
, is useful to get a refresher on the topic.
Goroutines and Channels
We will be using goroutines and channels to implement concurrent execution in our applications. I recommend going through the section on Concurrency in “A Tour of Go”: https://tour.golang.org/concurrency/1
. Pay special attention to the example use of select statements to wait on multiple channel communication operations.
Testing
We will be using the standard library's testing
package exclusively for writing all of the tests, and we will use Go test to drive all of the test executions. We have also used the excellent support provided by libraries such as net/http/httptest
to test HTTP clients and servers. Similar support is provided by gRPC libraries. In the last chapter, we will use a third-party package, https://github.com/testcontainers/testcontainers-go
, to create local testing environments using Docker Desktop.
In some of the tests, especially when writing command-line applications, we have adopted the style of “Table Driven Tests,” as described at https://github.com/golang/go/wiki/TableDrivenTests
, when writing the tests.
Summary
In this introduction to the book, you installed the software necessary to build the various applications to be used in the rest of the book. Then I introduced some of the conventions and assumptions made throughout the remainder of the book. Finally, I described the key language features with which you will need to be familiar to make the best use of the material in the book.
Great! You are now ready to start your journey with Chapter 1, where you will be learning how to build testable command-line applications.
CHAPTER 1 Writing Command-Line Applications
In this chapter, you will learn about the building blocks of writing command-line applications. You will use standard library packages to construct command-line interfaces, accept user input, and learn techniques to test your applications. Let's get started!
Your First Application
All command-line applications essentially perform the following steps:
Accept user input
Perform some validation
Use the input to perform some custom task
Present the result to the user; that is, a success or a failure
In a command-line application, an input can be specified by the user in several ways. Two common ways are as arguments when