Error-handling in Go

One of the tiresome aspects of Go is its standard idiom for error-handling. In that idiom, functions return errors, callers check those errors, and then take action accordingly. Typically, that action is for the caller to return the error to its own caller, gradually passing it up the call-stack for something else to handle. In practice, this results in a lot of boilerplate code that looks like this:

func MyFunc() (int, error) {
  n, err := someOtherFunc()
  if err != nil {
    return nil, err
  }

  if err := anotherFunc(n); if err != nil {
    return nil, err
  }
 
  return n, nil
}

The code above illustrates a couple of variations on the technique, but the pattern should be evident. Call after call, you check the err to see if it’s nil.

This has two implications. First, there’s a lot of redundancy; it can quickly become tedious typing the same code again and again. Second, it gets hard to see what’s going on with the code because it’s hidden behind all of this error handling code. I’ve found there are a couple of things I can do.

If I strive for functional cohesion, I tend to end up with functions that have fewer lines of code because they focus on specific tasks. As a result, I end up having to write less of this boilerplate code per function simply because there’s less code per function generally.

For more complex functions, such as those with procedural cohesion, I can often use some sort of centralized error to make the procedure in the function more apparent by hiding the error-handling. Let me provide a couple of examples of this.

Central Error Handling in a Builder

In a current project, I have a Builder type similar to the GoF builder pattern. Something like this:

type Builder struct{
...
}

func (b Builder) AddObjA(a ObjA) error {
...
}

func (b Builder) AddObjB(b ObjB) error {
...
}

func (b Builder) constructPartA() (Product, error) {
...
}

func (b Builder) constructPartB(p Product) (Product, error) {
...
}

func (b Builder) finishConstruction(p Product) (Product, error) {
...
}

func (b Builder) GetFinishedProduct() (Product, error) {
  product, err := b.constructPartA()
  if err != nil {
    return nil, err
  }
  product, err = b.constructPartB(product)
  if err != nil {
    return nil, err
  }
  return b.finishConstruction(product)
}

In the actual code, the GetFinishedProduct function does a lot more, but the idea is the same: GetFinishedProduct calls several functions in a specific sequence to build the final product from parts that were added to the builder earlier on. The sequence of GetFinishedProduct was obscured by all the error-handling code.

To make it clearer, I added a central error field to the Builder, and then adjusted my “construction” methods to check that error field before they did anything. If the error field was non-nil, the methods simply did nothing. As a result, my GetFinishedProduct could simply call the construction methods in sequence without worrying about their errors. It looks like this:

type Builder struct{
...
  err error
}

...

func (b Builder) constructPartA() Product {
  ...
  product, err := doSomeWork()
  if err != nil {
    b.err = err
    return nil
  }
  return product
}

func (b Builder) constructPartB(p Product) Product {
  if b.err != nil {
    return nil
  }
  ...
}

func (b Builder) finishConstruction(p Product) Product {
  if b.err != nil {
    return nil
  }
  ...
}

func (b Builder) GetFinishedProduct() (Product, error) {
  product := constructPartA()
  product = constructPartB(product)
  product = finishConstruction(product)
  return product, b.err
}

Error-handling is still going on, but it happens in construction functions that tend to be very focused, so they have few lines of code and thus less error-handling to do. Additionally, every construction function stores any of its errors in b.err; this puts the Builder into an “error” state so that future construction functions become no-ops. Each construction function (except the first) checks to see if the Builder is in an error state and simply skips any work if it is.

As a result of these changes, the GetFinishedProduct method becomes far simpler and its construction procedure is more apparent. It doesn’t need to do any error-handling itself since the construction functions are taking care of that. It simply returns whatever the construction functions produced along with the Builder’s error state.

Central Error Handling in a Function

I arrived at a similar approach recently using anonymous functions. I had a scenario where I was creating an implementation of the io.WriterTo interface, i.e. a function with the signature

func (o MyObject) WriteTo(w io.Writer) (int64, error) {...}

One of the challenges with this function was the need to count how many bytes I’d written when I was using other objects to do a lot of the writing and, depending on the methods called, I was getting back an int count of lines written in some cases and int64 in others. This, in addition to error handling, was making the code unwieldy.

What I arrived at was the use of some anonymous methods to help with the accumulation of total bytes written and managing the error-handling in a fashion similar to what I used in the Builder. The final product looked something like this:

func (o MyObject) WriteTo(w io.Writer) (int64, error) {
  total, err := int64(0), error(nil)

  print := func(s string) {
    if err != nil { return }
    var n int
    n, err = fmt.Fprint(w, s)
    total += int64(n)
  }
 
  write := func(wt io.WriterTo) {
    if err != nil { return }
    var n int64
    n, err = wt.WriteTo(w)
    total += n
  }

  // Actual procedure
  print(headerText)
  for _, child := range o.children { // children are io.WriterTo
    write(child)
  }
  print(footerText)

  return total, err
}

Here, the central error is a variable, err, that is accessible to the anonymous functions (assigned to the print and write variables) via closure. As in the Builder’s construction methods, these assign any errors to the err variable and they check to see if that variable is nil before doing any work, becoming “no-ops” if the error is non-nil.

This approach, again, allows the function to express its central procedure more clearly since it doesn’t have to handle errors after every function call.

The driver in this case wasn’t really the central error handling. The real driver was wanting a way to deal with the fact that fmt.Fprint returns the count of written bytes as an int while io.WriteTo returns it as an int64. This required having multiple variables to capture the intermediate counts before adding them to the total. That drove me toward the use of anonymous functions to wrap that work, and once they were in place, it was a simple step to centralizing the error handling.

Conclusion

While I wish that Go gave us the option to use structured exception handling (i.e. try…catch… blocks), there are ways to deal with its native approach without having to write a ton of redundant code in each function. Keep your functions functionally cohesive (i.e. focused on doing one thing) and they’ll have fewer lines of code, and hence, fewer calls that can result in errors. For functions with sequential or procedural cohesion, where the function’s work is more about calling other functions in sequence, look for opportunities to “centralize” your errors so that you can reduce the error-handling and make the procedure clearer.

Error-handling in Go

Go is not an object-oriented language

I’ve spent the better part of my career working with object-oriented languages. I was exposed to them first through Visual Basic. From there, I learned Java, then C#, and then C++. Along the way, I made a deliberate study of object-oriented design, drinking deeply from the design patterns promulgated by the Gang of Four, and learning to reshape everything I did in terms of object-oriented design. It’s the way I think about, decompose, and solve problems.

When I started learning Go (https://golang.org), I approached it from this same mindset, and at first it felt like I was making some traction. In time, though, I discovered it wasn’t working. I kept running up against brick walls where it felt like I was almost there, but the wall between what I was trying to accomplish and what Go could do was simply too high.

The problem wasn’t Go. The problem was that I was trying to treat Go like an object-oriented language when it isn’t. Some ideas that commonly appear in object-oriented languages, like interfaces, misled me. The sort of “pseudo-inheritance” that happens when you embed one structure into another made me feel like maybe I could treat the embedded structure as a base class and the embedding structure as a subclass. But while these things hinted at or “smelled” like object-orientation, they really weren’t.

I had to approach Go on its own terms. It is not an object-oriented language. Structures in Go aren’t not polymorphic the way that objects are in C# or C++. Embedding is not the same thing as subclassing. Structures are not classes.

I have a way to go still. 20+ years of an object-oriented habit is hard to break. But it is essential to succeed in using Go. As I approach Go on its own terms, I find myself less frustrated, and I start to see the patterns of use in Go that will help me achieve my goals. I start to see Go’s strengths more than its weaknesses.

My advice to anyone coming to Go from an object-oriented background like mine is to leave that thinking at the door lest you find yourself making things needlessly complicated and thoroughly frustrating. Approach Go with an open mind.

Go is not an object-oriented language

Last Lecture

I have a lot of life left ahead of me still, so this last lecture lacks the morbid overtones of some. This last lecture looks back at the lessons I’ve learned this semester and summarizes those lessons into some key ideas that I want to share and remember.

I came into this class not knowing what to expect. For years, I believed that I didn’t have what it would take to be an entrepreneur. Like many, my mental image of an entrepreneur includes people like Rockefeller, Ford, Gates, and Zuckerberg. These are people who took a small idea and spun it into a billion-dollar industry, and whose names are synonymous with success. Entrepreneurship, to me, was this idea of finding some gem of a novel idea that the world has never seen, shining it up, and building a fortune from it.

But what I’ve learned is that this isn’t the case. Most entrepreneurs will never be known. They’re the guys who run a lawn business, or a store in a strip mall, or a charity that changes lives. Entrepreneurship doesn’t require a brilliant new idea. It requires, instead, a person with a skill who wants to make the world better and works to make that happen. And that’s something that anyone can do.

Why aren’t we all entrepreneurs then? Because the road to successful entrepreneurship is long and arduous. The ingredient that makes entrepreneurial success isn’t the idea; it’s the perseverance that turns that idea into a reality. It’s the willingness to see obstacles not as roadblocks, but as opportunities to heroically clear the way for others. It’s the determination to pursue a dream across a plateau when there is little obvious progress until you find the payoff on the other side. It’s the readiness to face your personal dragons and sacrifice something of yourself to win the day.

In short, entrepreneurs recognize that God has given everyone gifts, and we all live in a world filled with danger and adventure and an opportunity to serve, and they take up their swords and run forward into that adventure not just once, but over and over again, day after day until they win. In one word, they persevere.

And what is success? It isn’t wealth – though wealth might come with it. Success is to have made the world a better place for your fellow man. It’s to have used all that talent and perseverance not to glorify yourself, but to have given others a glimpse of the glory of God. It’s to lift up others through service. It’s to have lived an ethical and moral life that sets an example and inspires others.

When I recognized the idea that entrepreneurship is a combination of service and perseverant application of my God-given gifts, I realized that I could be an entrepreneur whatever my circumstances. All around me are opportunities to serve. Even if I’m working for others, I can reach out to others – to coworkers and neighbors and family and friends – and say, “I see you have this problem. I have this skill that could help. Let me lend you a hand.” In time, some of that service might turn out to be a hidden business opportunity that I can turn into something bigger. But whether that happens or not, I will have made a difference if I have helped others.

What is true of me is true of us all. We can all be entrepreneurs in this way. God has given us all gifts and expects us to use them to help others; it only takes a willingness and dogged persistence on our part to do it. That is entrepreneurship, and that is the sum of what I have learned, and that is my last lecture for this course.

Last Lecture

Summary for Week 12

This is the penultimate week for the class, and I can feel it winding down. This week, there were two articles we read that really intrigued me. They both dealt with some of the practical matters of embarking on an entrepreneurial career, whereas most of the class to date has reflected on the spiritual and emotional aspects of entrepreneurship.

The first article, Identifying and Exploiting the Right Entrepreneurial Opportunity…for You, dealt with the practical matter of finding an entrepreneurial opportunity. As with other readings and videos this semester, it identified that opportunity as the intersection of three circles; in this case: Personal Satisfaction, Economic Feasibilities, and Societal Needs. In other contexts, opportunities have been shown to exist at the intersection of Skills, Aspirations, and Market Realities/Needs. I think that these two views align nicely: Personal Satisfaction = Aspirations, Societal Needs = Market Realities, and Economic Feasibilities = Skills. Basically, an entrepreneurial opportunity exists where what you can do overlaps with what you want to do and what the market needs.

What was more useful to me, though, was the article’s definition of and approach toward ideas, possibilities, and opportunities. I’ve struggled until now feeling like I couldn’t come up with good ideas. Really, though, coming up with ideas is the easy part; we do it all the time. Any time we run into a problem and say, “Wouldn’t it be great if…” or “I sure wish that…,” we’ve come up with an idea. The challenge is filtering those ideas to see if there really is an entrepreneurial possibility there, and then filtering those possibilities further to see if they are really opportunities. Ideas become possibilities when there is a Societal Need for an idea/solution and it is Economically Feasible. Possibilities become opportunities when they give us Personal Satisfaction. The article gives advice on business questions to ask about your ideas, and personal questions to ask about your possibilities. This approach is something I can apply not just to entrepreneurship, but to my traditional career as well.

The second article, Recognizing and Shaping Opportunities, continues the theme of finding entrepreneurial opportunities. It provided concrete examples of the approaches a few entrepreneurs used to turn ideas into real opportunities. It showed that ideas can come from many different places and the process of turning an idea into an opportunity can differ from one idea to the next. In some cases, ideas come from adapting an existing business; in others, they come from transplanting a business from one market to another; in yet others, they come from transforming an existing or failed idea into something better. The process of turning an idea into a business might be deliberate, but it can also be organic. In any case, it is not common for an opportunity to come into being; there is a transformative process that must go on.

With this in mind, I realize that ideas are not the challenge; they are abundant. The challenge is working with them and shaping them into something that can work, and then having the guts to give it a shot.

 

Summary for Week 12

Summary for Week 10

It’s been a unique week this week, one where I’ve had the opportunity to put into action in my life the lessons of the week. An Acton guide, Life, Liberty, and the Pursuit of Happiness, reiterated the vision the Acton Institute promulgates: to view our lives as a heroic journey.

This is a point of view, an idea that we have to develop within ourselves, a paradigm from which to view our actions and to derive our motivation. They’ve modeled their book that we’ve been reading, the Hero’s Journey, along the line of this philosophy.

This week, I’ve had an opportunity to see some of my actions in this light, and it has helped me to persevere in work and take, if not pride, at least a sense of value and purpose in what I had to do.

Continue reading “Summary for Week 10”

Summary for Week 10

Summary for Week 9

I learned a couple of things this week, both from the same article, “The Heart of Entrepreneurship” (Howard H. Stevenson and David E. Gumpert, Harvard Business Review, March 1985). This article dove into a description of entrepreneurship, and – in particular – what distinguishes entrepreneurial managers from administrative managers.

For me, the revelation was that I am more of an entrepreneur than I thought. True, there are still aspects that I need to develop, but in general, I lean toward the entrepreneurial end of the spectrum they describe in the article.

If I were to sum up their definition of an entrepreneur, it is someone who does not allow uncertainty and minimum resources to keep them from making a go at something. Unlike the administrator, who needs to ensure success is a foregone conclusion by ensuring all the Ts are crossed and the Is dotted and that a full contingent of resources is at hand, an entrepreneur will make do with the minimum needed to start and with confidence in his or her ability to succeed.

Continue reading “Summary for Week 9”

Summary for Week 9