Three Productive Go Patterns to Put on Your Radar

By most definitions, with just 26 keywords, a terse spec, and a commitment to orthogonal features, Go is a simple language. Go programmers are expected to use the basic building blocks offered by the language to compose more complex abstractions. Over time, best-in-class solutions to frequently encountered problems tend to be discovered, shared, and replicated. These design patterns draw heritage from other languages, but often look and feel distinct in Go. I’d like to cast a spotlight on three patterns I use over and over again.

The Dependency Injection Pattern

Dependency injection (DI) is actually an umbrella term that can mean vastly different things depending on context. The core idea is this: Give or inject dependencies to a component, rather than have the component take dependencies from the environment. Beyond that, things can get a little complicated. Some people use DI to refer to dependency injection frameworks, typically a package or object into which you register dependencies and later inject them into components that use them, usually by some key schema. But this style of DI isn’t a good match for Go, primarily because Go lacks the dynamic typing required to serve a literate API. Most DI frameworks in Go resort to stringly typed keys (meaning variables are often typed as strings), and rely on reflection to reify the dependencies to concrete types or interfaces, which is always a red flag.

Instead, a more basic version of DI is particularly well-suited to Go programs. The inspiration comes from functional programming, specifically, the idea of closure scoping. And it’s nothing special, really: Just provide all the dependencies to a component as line items in the component’s constructor.

// NewHandler constructs and returns a useable request handler.

func NewHandler(

db *sql.DB,

requestDuration *metrics.Histogram,

logger *log.Logger,

) *Handler {

return &Handler{

db:     db,

dur:    requestDuration,

logger: logger,

}

}

The clear consequence of this pattern is that constructors begin to get very long, especially as business capability grows. That’s a cost. But there’s also a notable benefit. Namely, there’s great virtue in making dependencies explicit at the callsite, especially to future readers and maintainers of your code. It’s immediately obvious that the Handler takes and uses a database, a histogram, and a logger. There’s no need to hunt down dependency relationships far from the site of construction.

The writer pays a cost of keystrokes, but the reader receives the benefit of comprehension. Outside of hobby projects, we know that code is read far more often than it is written. It’s reasonable, then, to optimize for the benefit of reader—even if it comes at some expense to the writer. But we have some tricks up our sleeve to make long constructors for large components more tractable.

One approach is to use a tightly scoped config struct, containing only those dependencies used by the specific component. It’s typical to omit individual fields when building a struct, so constructors should detect nils, when appropriate, and provide sane default alternatives.

// NewHandler constructs and returns a useable request handler.

func NewHandler(c HandlerConfig) *Handler {

if c.RequestDuration == nil {

c.RequestDuration = metrics.NewNopHistogram()

}

if c.Logger == nil {

c.Logger = log.NewNopLogger()

}

return &Handler{

db:     c.DB,

dur:    c.RequestDuration,

logger: c.Logger,

}

}

// HandlerConfig captures the dependencies used by the Handler.

type HandlerConfig struct {

// DB is the backing SQL data store. Required.

DB *sql.DB



// RequestDuration will receive observations in seconds.

// Optional; if nil, a no-op histogram will be used.

RequestDuration *metrics.Histogram



// Logger is used to log warnings unsuitable for clients.

// Optional; if nil, a no-op logger will be used.

Logger *log.Logger

}

If a component has a few required dependencies and many optional dependencies, the functional options idiom may be a good fit.

// NewHandler constructs and returns a useable request handler.

func NewHandler(db *sql.DB, options ...HandlerOption) *Handler {

h := &Handler{

db:     c.DB,

dur:    metrics.NewNopHistogram(),

logger: log.NewNopLogger(),

}

for _, option := range options {

option(h)

}

return h

}

// HandlerOption sets an option on the Handler.

type HandlerOption func(*Handler)

// WithRequestDuration injects a histogram to receive observations in seconds.

// By default, a no-op histogram will be used.

func WithRequestDuration(dur *metrics.Histogram) HandlerOption {

return func(h *Handler) { h.dur = dur }

}

// WithLogger injects a logger to log warnings unsuitable for clients.

// By default, a no-op logger will be used.

func WithLogger(logger *log.Logger) HandlerOption {

return func(h *Handler) { h.logger = logger }

}

By using this simplified DI pattern, we’ve made the dependency graph explicit and avoided hiding dependencies in global state. It’s also worth using a simplified definition of dependency—that is, nothing more than something that a component uses to do its work. By this definition, loggers and metrics are clearly dependencies. So by extension, they should be treated identically to other dependencies. This can seem a bit awkward at first, especially when we’re used to thinking of e.g. loggers as incidental or ubiquitous. But by lifting them up to the regular DI mechanism, we do more than establish a consistent language for expressing needs-a relationships. We’ve made our components more testable by isolating them from the shared global environment. Good design patterns tend to have this effect—not only improving the thing they’re designed to improve, but also having positive knock-on effects throughout the program.

The Client-Side Interface Pattern

Concretely, interfaces are nothing more than a collection of methods that types can choose to implement. But semantically, interfaces are much more. They define behavioral contracts between components in a system. Understanding interfaces as contracts helps us decide where and how to define them. And just as contract testing in microservice architectures teaches us that the right place to write a contract is often with the consumer.

Consider a package with a type. Go programmers frequently model that type and its constructor like the following.

package foo

// widget is an unexported concrete type.

type widget struct{ /* ... */ }


func (w *widget) Bop(int) int                     { /* ... */ }

func (w *widget) Twist(string) ([]float64, error) { /* ... */ }

func (w *widget) Pull() (string, error)           { /* ... */ }



// Widget is an exported interface.

type Widget interface {

Bop(int) int

Twist(string) ([]float64, error)

Pull() (string, error)

}



// NewWidget constructor returns the interface.

func NewWidget() Widget { /* ... */ }

In our contract model of interfaces, this establishes the Widget contract alongside the type that implements it. But how can we predict which methods consumers actually want to use? Especially as the type grows functionality and our interface grows methods, we lose utility. The bigger the interface, the weaker the abstraction.

Instead, consider having your constructors return concrete types and letting package consumers define their own interfaces as required. For example, consider a client that only needs to Bop a Widget.

func main() {

w := foo.NewWidget() // returns a concrete *foo.Widget

process(w)           // takes a bopper, which *foo.Widget satisfies

}



// bopper models part of foo.Widget.

type bopper interface {

Bop(int) int

}



func process(b bopper) {

println(b.Bop(123))

}

The returned type is concrete, so all of its methods are available to the caller. The caller is free to narrow its scope of interest by capturing the interesting methods of Widget in an interface and using that interface locally. In so doing the caller defines a contract between itself and package foo: NewWidget should always produce something that I can Bop. And even better, that contract is enforced by the compiler. If NewWidget ever stops being Boppable, I’ll see errors at build time.

More icing on the cake: Testing the process function is now a lot easier, as we don’t need to construct a real Widget. We just need to pass it something that can be Bopped—probably a fake or mock struct, with predictable behavior. And this all aligns with the Code Review Comments guidelines around interfaces, which state that:

Go interfaces generally belong in the package that uses values of the interface type, not the package that implements those values. The implementing package should return concrete (usually pointer or struct) types: that way, new methods can be added to implementations without requiring extensive refactoring.

Sometimes there is a case for defining interfaces near to concrete types. For example, in the standard library, package hash defines a Hash interface, which types in subsidiary packages implement. Although the interface is defined in the producer, the semantics are the same: a tightly scoped contract that the package commits to supporting. If your contract is similarly tightly scoped and satisfied by different types with different implementations, then it may make sense to include it alongside those implementations as a signal of intent to your consumers.

The Actor Pattern

Like dependency injection, the idea of the actor pattern can mean wildly different things to different people. But at the core, it’s not much more than an autonomous component that receives input and probably produces output. In Go, we learned pretty early on that a great way to model an actor is as an infinitely looping function selecting on a block of channels. The goroutine acts as a synchronization point for state mutations in the actor, in effect making the loop body single threaded—a huge win for clarity and comprehensibility. We typically name this function `run` or `loop` and define it as a method on a struct type that holds the channels.

type Actor struct {

eventc   chan Event

requestc chan reqRes

quitc    chan struct{}

}

func (a *Actor) loop() {

for {

select {

case e := <-eventc:

a.consumeEvent(e)

case r := <-requestc:

res, err := a.handleRequest(r.req)

r.resc <- resErr{res, err}

case <-quitc:

return

}

}

}

Finally, we push onto those channels in our exported methods, forming our public API, which is naturally goroutine-safe.

func (a *Actor) SendEvent(e Event) {

a.eventc <- e

}

func (a *Actor) MakeRequest(r *Request) (*Response, error) {

resc := make(chan resErr)

a.requestc <- reqRes{req: r, resc: resc}

res := <-resc

return res.res, res.err

}


func (a *Actor) Stop() {

close(a.quitc)

}


type reqRes struct {

req  *Request

resc chan resErr

}


type resErr struct {

res *Response

err error

}

This works great in a lot of circumstances. But it does require us to define a unique channel per distinct public API method. It also makes things a little tricky when we need to return information to the caller. In this example, we use an intermediating `reqRes` type, with a response channel, but there are other possibilities.

There is an interesting alternative. Rather than having one channel per method, we use a single channel of unadorned functions. In the loop method, we simply execute every function that arrives; the exported methods define their functionality inline.

type Actor struct {

actionc chan func()

quitc   chan struct{}

}


func (a *Actor) loop() {

for {

select {

case f := <-actionc:

f()

case <-quitc:

return

}

}

}


func (a *Actor) SendEvent(e Event) {

a.actionc <- func() {

a.consumeEvent(e)

}

}

func (a *Actor) HandleRequest(r *Request) (res *Response, err error) {

done := make(chan struct{})

a.actionc <- func() {

defer close(done) // outer func shouldn't return before values are set

res, err = a.handleRequest(r)

}

<-done

}

This style carries several advantages:

  1. There are fewer mechanical bits in the actor.
  2. We have much more freedom in the public API methods to return values to callers.
  3. Business logic is defined in the corresponding public API method, rather than hidden in an unexported loop method.

Your Patterns

The dependency injection, client-side interface, and actor patterns can improve your productivity, offer you more freedom, and optimize your programming. Rather than default to basic solutions, get creative and try out one or all of these three distinct Go patterns.

AppDynamics Spring ‘17 Enables APM for More Developers

There have long been silos of information and talent within enterprises as each team works on their part of the big puzzle, building applications to move the business forward. However, the pieces of the puzzle are getting bigger in number with the adoption of microservices and multi-cloud strategies, and more complex to assemble as multiple frameworks and programming languages coexist within an application. In this complex ecosystem, developers, operations, and business teams do their best to align and anticipate what the requirements and interdependencies are, but many enterprises don’t realize the full picture.

To build a successful business in this era of high velocity, high customer expectations, and increasing complexity, enterprises must create trusted teams across business, development, and operations (BizDevOps) to execute at peak effectiveness. Today, AppDynamics releases its Spring ‘17 release with features and capabilities that will enable closer collaboration between these teams.

Developer Toolkit

As part of Spring ‘17, the new Developer Toolkit brings APM into more stages of the software development lifecycle to let developers optimize for performance early in their process. Business Transaction (BT) developer mode enables a single BT to be isolated and captured so developers can test specific functionality or microservices without artificially increasing the overhead of the entire application. BT developer mode enables them to develop and release faster with less risk by tuning functionality early. Using Business Transaction Live Mode, operations and development can use sampled live application data to fine tune their BTs and ensure all business relevant values and interactions are captured.

For mobile developers, Spring ‘17’s toolkit brings an arsenal of new capabilities, including Xamarin support, allowing them to release these mobile applications faster and with confidence, having performance and crash information for this new framework. Additionally, mobile developers get automatically configured smart alerts that identify new crash types on each version of the application and alert them with aggregated root cause as well as each individual user and session impacted to resolve faster.

The AppDynamics Developer Toolkit also brings support for Go language for developers to instrument their applications. This new capability allows for full visibility into the entire application tiers so every single customer interaction is traced from touch to code to infrastructure.

Deeper and wider with Enterprise iQ

Enterprise iQ is at the core of the AppDynamics App iQ platform and allows it to scale with the largest and most complex environments. In Spring ‘17, we’re expanding agent and license administration capabilities to support our largest deployments with over 100,000 agents. The new Universal Agent for Java and Server allow for ease of deployment and upgrades across large or dynamic environments. New license management capabilities streamline administration across enterprises and give the ability to set license quotas and view consumption across the organization.

Bringing Business iQ to development

AppDynamics’ new Spring ‘17 brings a wealth of new features and capabilities to help teams build trust and align initiatives. From tying mobile application quality to business outcomes using the Developer Toolkit to providing streamlined administration and management across the largest and more complex environments with Enterprise iQ, the Spring ‘17 release focuses on enabling application success across the organization. With the App iQ platform, enterprises are able to stay at the forefront of innovation and release faster and with less risk to stay ahead of the competition.

Go for Performance with AppDynamics for Go

When Google gets frustrated with the complexities and slow speed of Internet technology, they do what Google does best — they solve for it. They set out to create a new language that was fast, worked on large server systems, scaled well, and could run concurrency smoothly. The result is Go. And, when our customers speak, AppDynamics does what it does best — we listen!

AppDynamics announces its official support for Go

We’re excited to officially announce our support for Go! In our Spring ‘17 release, application teams may take advantage of the AppDynamics platform now available for their Go apps. Monitor your Go applications in real time, correlate transactions across your distributed environment, and diagnose performance bottlenecks while running in a live production or development environment.

What Is Go?

Go was created in 2007 by Ken Thompson, Rob Pike, and Robert Griesemer, along with a number of contributors. One of the most prominent is Thompson, who also wrote the B programming language and was instrumental in the design and introduction of the Unix operating system.

Google formally introduced Go in November of 2009 and began using it in some of their production systems. Other companies soon began deploying it as well. There is an open-source version called “gc” that works on a number of platforms including UNIX, Windows, BSD, and Linux. As of the beginning of 2015, it also works on smartphones and mobile devices. A second version, “gccgo,” is a frontend component for the GNU Compiler Collection (GCC).

The language began as an experiment among several Google programmers. They wanted to create a new language that took care of some of the problems of other languages while keeping their positive attributes. They wanted it to be statically typed and highly scalable. It also had to be legible and productive, foregoing many of the keywords and repetition of other languages. They also wanted to handle networking and multiprocessing quickly and easily. The engineers who worked on the project had a deep-seated disdain for the complexity of C++. Go shares some of the characteristics of C, but is built to be simpler, safer, and more concise.

Go uses unique approaches to handle the problems common in other languages. It has built-in concurrency, channels, and lightweight processes called goroutines. The toolchain creates statically linked binaries with no dependencies from external sources. It uses an interface instead of virtual inheritance and type embedding rather than non-virtual inheritance. Go is statically typed, open-source, and compiled. Published under BSD licensing, it also has (among other features):

  • Memory safety components
  • Garbage collection
  • Concurrent programming
  • Structural typing

Built for Concurrency

Go is made for concurrency. While older languages were designed with only a single processor in mind, Go runs efficiently on today’s multicore processors with parallel processing.

Concurrent does not necessarily mean two processes are running at the same time (i.e., parallel processing). Concurrency refers to two tasks being able to begin, run, and end in overlapping periods of time (e.g., they may not actually ever run at the same time). In contrast, parallelism refers to two tasks operating at the same time, as you find in a multicore machine. An example of concurrency is when a single-core computer handles multitasking, which is what makes Go ideal for high-load systems such as an API handling massive amounts of requests from mobile devices or browser calls.

Extensive Libraries

Go is verbose. You have to use a lot of code to complete commands. It also has a lot of libraries, to name a few:

  • http
  • regular expressions
  • json
  • file CRUD operations

On the flip side, you have to import these libraries, because it won’t compile if you don’t. This was made to keep Go as lean as possible. It’s fast, and built-in concurrency lets you run a lot of simultaneous processes.

Companies Using Go

Thousands of companies around the world currently use Go, including:

  • Google (of course)
  • Twitter
  • BBC Worldwide
  • Comcast
  • eBay
  • IBM

Smaller companies are also on board, including:

  • MalwareBytes
  • Shutterfly
  • Square
  • Zynga

Here are some real world examples:

Dropbox

After using Python in the early years of its operation, Dropbox realized their success and growing customer base required a language that could scale better and handle bigger loads. In 2013, they began to move important backend operations from Python to Go. The goal was to improve their concurrency and execute code faster, successfully deploying 200,000 lines of Go computer code.

At the time, they were somewhat hindered by Go’s lack of deep libraries, a characteristic of its youth and newness. The Dropbox developers took on the tasks themselves and began to create libraries for Memcache, connection management, and other purposes. They contributed to the Go open-source effort by making the libraries available to other programmers and companies interested in building production systems that can scale quickly and effectively.

SendGrid

Around the same time Dropbox changed over to Go, cloud-based email service SendGrid decided to do the same. In the first years of operation, their backend consisted mainly of Perl/AnyEvent, later changing to Python/Twisted. They considered the Gevent/Python framework but realized it wouldn’t do what they needed. They narrowed it down between Go, Java, and Scala.

Because they handle more than 500 million email messages every day, one of their biggest challenges is concurrency. Go’s ability to handle concurrent asynchronous programming was a major factor in their decision to use it. At the same time, it was a language their developers were very interested in using, unlike others that they felt like they had to fight every day. In fact, several developers were so excited about it that they began to teach it to themselves and experiment on their own. This turned out to be a decisive factor because the company realized their team was already using it in their off hours and would be enthusiastic about using the language every day.

StatHat

Numerotron is a small firm that developed a program called StatHat to allow developers and engineers to track events and statistics right in their code. StatHat can be used in twelve different computer languages including Go and HTML, and it can be easily deployed by a wide variety of professionals including backend engineers and designers.

Patrick Crosby, founder of Numerotron, said they chose Go for StatHat because it met many of their criteria, including great performance, many connections on one machine, fast HTML templating, quick startup and recompilation, extensive libraries, and open-source.

Go Vs. Other Languages

Python

Part of the appeal of Python is that it is so versatile (e.g., scripting vs. object-oriented and functional programming). It can run on any platform that includes a Python interpreter. It is used worldwide for both large and small application development.

It is very verbose, and it’s made to solve specific problems without constraints. It lets you build programs rapidly and then modify those programs to create powerful solutions. It’s simple to learn but takes time to fully understand. One of Go’s major strengths is its ability to handle concurrency efficiently using channels and goroutines.

C

In many ways, Go is a new version of C. The Go creators attempted to clean up the problem areas of C and take out functionalities that were creating bottlenecks. Go is simpler than C, although the primitives are more detailed than those found in Java and C++.

Java

Go is a young language, so the contributors were able to incorporate features that make it extremely effective in meeting the demands of modern web-scale traffic. While Java continues to be one of the major languages used for application development, older, similar languages have some limitations in scale.

The Future Is Bright

Go is an effective solution for projects that need scale to handle massive amounts of traffic. As the amount of data continues to expand, Go is a modern solution to building applications that can handle the demand. Expect to see Go adopted more and more as the world moves forward to meet the demands of modern web-scale traffic.

Learn more about all popular programming languages and frameworks that AppDynamics covers, including Java, .NET, Node.js, PHP, Python, and C/C++ and now, Go!