Tejas Manohar


On Golang’s `defer` keyword

Golang has a defer keyword, which “defers” the execution of a function until the surrounding function returns. Multiple defers are stacked last-in first-out so that the most recently deferred function is run first.

Let’s check out an example of this in practice.

func writeLogs(c chan []string) {
defer stats.Duration("write_logs_fn", time.Now()) // order: 4

file, err := os.Create("request.log", filename)
if err != nil {
defer file.Close() // order: 3

writer := csv.NewWriter(file)
defer writer.Flush() // order: 2

for line := range c {
err := writer.Write(line)
if err != nil {
defer stats.Increment("wrote_line") // order: 1

At first glance, you may ask —

How is this different from the finally keyword in other languages?

From Java Docs,

finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

Well, let’s compare defer to the following Java example using finally.

static void writeLogs(List<List<String>> lines) throws Exception {
Date start = new Date();
FileWriter writer = null;
int lineCount = 0;

try {
writer = new FileWriter("request.log");
// non-buffered input
for (int i = 0; i < lines.size(); i++) {
// naive CSV writer
for (value : lines[i]) {
} finally {
if (writer != null) {
    stats.increment("wrote_line", lineCount);


Off the bat, you can identify a handful of “gotchas”:

  • Separation of similar concerns. finally separates alike concerns. Because you can’t declare that you’d like to close a file when you open it, you can’t effectively plan ahead; instead, you have to remember to fit it into the surrounding context. I can’t count how many programmer errors I’ve seen due to separating alike concerns.
  • Interruptions. finally requires immediate, exclusive, and interrupting attention, whereas defer allows you to plan ahead without interrupting your usual control flow. In the Go example, we could easily defer incrementing a metric as we read each line, whereas in Java, deferring multiple calls would require more advanced control flow, like a queue, so instead, we have to reorganize our flow by aggregating.
  • Readability. finally enforces nesting, which hurts overall readability, whereas defer can be dropped in inline. Notice the != null check, counters, and separate definition from declaration. Holistically, none of these characteristics are meaningful to a reader, but they’re forced to parse it due to the nested structure.
  • Imperative vs. Declarative. This may not be a point you were expecting to flow to Go’s side, but altogether, defer is more declarative. You tell the computer that you want to do X, Y, Z rather than how.

Alike most technical debt, I’ve found that each of these “gotchas” becomes more apparent as the complexity of your function grows.


Though unconventional, Go’s defer is pure brilliance. It disguises a generally complex control flow with a simple, universal one that both helps readability and keeps things declarative (two factors that are not mutually inclusive). It’s far superior to traditional teardown via finally, and you should almost always prefer it to rolling out your own control flow.

If you found this interesting, consider following me on Twitter, where I share my experiences with Go amongst other things.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising &sponsorship opportunities.
To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Tejas Manohar

Topics of interest

More Related Stories