How To Organize Golang Cloud Functions Code
Tags: go, serverless, devops, googlecloud
Published: 2020-12-07 15:21:43

---
title: How To Organize Golang Cloud Functions Code
published: true
description: A way of organizing Golang code for Cloud Functions
tags: go, serverless, devops, googlecloud
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/fqx9u7ish5ofdj0hg8ni.png
---

On our journey to build the next billion dollar app, we decided to use Cloud Functions to write the backend logic in Golang. So thinking about how the hell we were gonna organize the code, was one of the first things we went through.

The only thing we knew for sure, was that we wanted all code in a single repository, in order to speed up the development velocity.

This is what we came up with:

## A Go module per function

```text
.
└── functions
    ├── awesome
    │   ├── awesome.go
    │   ├── go.mod
    │   └── go.sum
    └── marvelous
        ├── marvelous.go
        ├── go.mod
        └── go.sum
```

Having a Go module per function enables every function to manage its dependencies independently.

This avoids annoyances like making other functions stick to an older package version, or breaking other functions by upgrading major versions.

## Relative imports

However, there's shared logic between functions that needs to be in common packages, and not having the functions within the same module makes it tricky to use them from the functions code.

Here's where the `replace` directive comes to the rescue, allowing us point an import path to another module located in VCS (GitHub or elsewhere), or on the local filesystem with a relative or absolute file path.

```go
module github.com/example/functions/marvelous

go 1.13

replace github.com/example/functions/pkg => ../../pkg
```

```text
.
├── functions
│   ├── awesome
│   │   ├── awesome.go
│   │   ├── go.mod
│   │   └── go.sum
│   └── marvelous
│       ├── marvelous.go
│       ├── go.mod
│       └── go.sum
└── pkg
    ├── compliments
    │   └── compliments.go
    └── go.mod
```

```go
package marvelous

import (
	"fmt"
	"net/http"
	"github.com/example/compliments"
)

func CallMeMarvelous(w http.ResponseWriter, r *http.Request) {
    text := compliments.Say("marvelous")
    fmt.Fprint(w, text)
}
```

## Vendor directory

Even though for development purposes this will work just fine, when the time to deploy the function comes, this won't cut it.

Executing `gcloud functions deploy` from the function directory, won't upload the `pkg` directory since it's located in a higher level, causing the above error:

```text
OperationError: code=13, message=Build failed: go: github.com/example/functions/pkg@v0.0.0-00010101000000-000000000000: parsing /pkg/go.mod: open /pkg/go.mod: no such file or directory; Error ID: 03a1e2f7
```

To avoid this we create the vendor directory before deploying:

```sh
go mod vendor
```

and a `.gcloudignore` file, to use the vendor approach by not uploading the `go.mod` and `go.sum` files.

```text
go.mod
go.sum
```

Now all the function needs is to be built and run is within the directory. Additionally the `**/vendor` path can be added to the root `.gitignore`.

## Conclusion

We are not Golang experts and it might not be a perfect approach, but it's the most elegant we found so far.