E


Even though Go has only been a language since 2009, it has quickly become one of the leading languages to write web applications with.

This is partially due because Go was modeled for distributed computing. Unlike other languages which support concurrency through the standard library or some add-on module, Go’s concurrency is built-in to the language itself.

While writing web applications is fairly easy to do in Go (thanks in part to its marvelous concurrency and small, easy to grasp language) it occasionally causes some issues you don’t find in traditional web-application languages such as PHP or Python.

Go is, in the traditional sense, compiled. Much like C, you have to compile the .go files into a binary before you can run the application. Unfortunately, this causes complications when you want to add or modify running code.

While writing a web app for Sermo Digital, I wanted to be able to modify a template file and see the changes live without recompiling my binaries. In most languages it’d be easy enough—simply modify the file and the next time the program reads the file you’ll have the updated version. Super simple.

However, with Go that’s not possible without (somehow) gracefully shutting down the server, recompiling the binary, and restarting the server… Or so I thought.


Go provides a lot of systems support; that is, you have access to a lot of the same APIs that you would in a typical systems programming language like C. One of these is inotify.

Per man7, “The inotify API provides a mechanism for monitoring filesystem events… individual files, or to monitor directories.” Keep that in mind.

In most Go web apps, templates are used to render specific views. For example, a template for a blog post might look like this:

const postTemplateText = `<title>{{.PostTitle}}</title>
<section>{{.PostContent}}</section>
<aside>{{.AuthorByline}}</aside>`

func handler(w http.ResponseWriter, r *http.Request) {
	t := template.New("top")
	t, err := t.Parse(postTemplateText)
	if err != nil {
		// handle error
	}

	data := struct{
		PostTitle string
		PostContent string
		AuthorByline string
	} {
		PostTitle:    // yadda
		PostContent:  // yadda
		AuthorByline: // yadda
	}
	t.Execute(w, data)
}

However, this quickly becomes hard to maintain. Do all the template text variables have the same naming scheme? Do you repeat logic? Do you know in which files the variables reside? (That last one is key if you separate your app logic into different files/directories. Having run ag 'const postTemplateText' to find the variable gets old quick. Also, ag.)

The answer to this problem is to use maps.

So, instead of this:

const (
	postTemplateText      = `...`
	indexTemplateText     = `...`
	...
	aboutPageTemplateText = `...`
)

you end up with this

var templateMap = map[string]*template.Template{
	"post":  // parse template
	"index": // parse template
	...
	"about": // parse template
}

which is much more maintainable.

Interestingly enough, this approach also opens the door to automagically hot-reloading template files.

Back to inotify. It just so happens that Go provides support for creating templates from physical files in the same way that it does string variables. Putting two and two together, we can use inotify to alert our binary when we’ve altered a template file and have the binary slurp the modified file up into memory, replacing the old template.


Let’s start from the top. First, we need to declare our map that’ll hold our template files. However, since we’re modifying map values while we’re reading them, we need to add a mutex lock to protect our map. Race conditions suck, and they’re prone to sneakily corrupting your data without you knowing.

// Reloader is a collection of templates.
type Reloader struct {
	templates
	*sync.RWMutex
}

Simple enough. Now let’s add methods to retrieve our templates.

// Reloader is a collection of templates.
type Reloader struct {
	templates map[string]*template.Template
	*sync.RWMutex
}

func (r *Reloader) Get(name string) *template.Template {
	r.RLock()
	defer r.RUnlock()
	if t, ok := r.templates[name]; ok {
		return t
	}
	return nil
}

Great. Now we have a worry-free method of retrieving our templates. We could modify that a bit more to remove the costly defer, but for the sake of this simple example let’s not.

Now let’s add inotify in so we can keep track of file changes.

// Reloader is a collection of templates.
type Reloader struct {
	templates map[string]*template.Template

	*inotify.Watcher
	*sync.RWMutex
}

// Watch calls a goroutine that waits for inotify events and will hot-swap
// (reload) the modified template.
func (r *Reloader) Watch() {
	go func() {
		for {
			select {
			case evt := <-r.Watcher.Event:
				if eventIsWanted(evt.Mask) {
					fmt.Printf("File: %s Event: %s. Hot reloading.\n",
						evt.Name, evt.String())

					// do reload
				}
			case err := <-r.Watcher.Error:
				fmt.Println(err)
			}
		}
	}()
}

// eventIsWanted returns true if a modified or created file notification is
// received.
func eventIsWanted(mask uint32) bool {
	return mask&inotify.IN_MODIFY != 0 ||
		mask&inotify.IN_CREATE != 0
}

// Get retrieves a template with the given name from the internal map.
func (r *Reloader) Get(name string) *template.Template {
	r.RLock()
	defer r.RUnlock()
	if t, ok := r.templates[name]; ok {
		return t
	}
	return nil
}

Boom. Now that we have a way to find out when the physical files are modified, let’s add the actual reload function. For brevity’s sake I’ll stick to only writing out the additions and modifications from now on.

var (
	templatePath = filepath.Join(path, to, directory, containing, templates)
	templateExt = ".gohtml"
)
func (r *Reloader) reload(name string) error {
	// Some sort of check to make sure we don't overwrite our "base" template
	// files which I'll show you later.
	// if name == base { doSomething() }

	if len(name) >= len(templateExt) &&
		name[len(name)-len(templateExt):] == templateExt {

		tmpl := template.Must(template.ParseFiles(name))

		// Gather what would be the key in our template map.
		// 'name' is in the format: "path/identifier.extension",
		// so trim the 'path/' and the '.extension' to get the
		// name (minus new extension) used inside of our map.
		key := name[len(TemplatePath) : len(name)-len(templateExt)]

		r.Lock()
		r.templates[key] = tmpl
		r.Unlock()

		return nil
	}

	return fmt.Errorf("Unable to reload file %s\n", name)
}

I mentioned “base” files in the snippet above. What I meant is this:

// Creates a template stored in 'tmpl' whose "base" is "base.gohtml".
// If, in the above snippet, we were to modify base.gohtml we would run into
// a panic that says something along the lines of "redefinition of template
// blah blah blah".
tmpl := template.Must(template.ParseFiles("index.gohtml"), "base.gohtml")

// In order to circumvent this, we write _something_ like this and
// rename our old reload function to doReload.
func (r *Reloader) reload(name string) error {
	if name == "name_of_our_base_file" {
		var err error

		// Reload all with the updated "base".
		for name, _ := range r.templates {
			if err = r.doReload(name); err != nil {
				glog.Errorln(err)
			}
		}
		return err
	}

	return r.doReload(name)
}

And that’s essentially it! Short, sweet, and to the point. (While writing this my editor is only at line 255, including all the extra markdown code!)

If you’d like the full source of the code here (sans the doReload method), you can find it in /r/golang or in this Gist.

At Sermo Digital we use a slightly enhanced version of this hot reloading. We use a fixed-size array, have some helper functions that allow maps with functions, and a lot more code that makes using the reloading system a lot nicer. (And faster!)