DEV Community

Cover image for How To Organize Golang Cloud Functions Code
Marco Davalos
Marco Davalos

Posted on • Edited on

4 3

How To Organize Golang Cloud Functions Code

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

.
└── functions
    ├── awesome
    │   ├── awesome.go
    │   ├── go.mod
    │   └── go.sum
    └── marvelous
        ├── marvelous.go
        ├── go.mod
        └── go.sum
Enter fullscreen mode Exit fullscreen mode

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.

module github.com/example/functions/marvelous

go 1.13

replace github.com/example/functions/pkg => ../../pkg
Enter fullscreen mode Exit fullscreen mode
.
├── functions
│   ├── awesome
│   │   ├── awesome.go
│   │   ├── go.mod
│   │   └── go.sum
│   └── marvelous
│       ├── marvelous.go
│       ├── go.mod
│       └── go.sum
└── pkg
    ├── compliments
    │   └── compliments.go
    └── go.mod
Enter fullscreen mode Exit fullscreen mode
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)
}
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

To avoid this we create the vendor directory before deploying:

go mod vendor
Enter fullscreen mode Exit fullscreen mode

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

go.mod
go.sum
Enter fullscreen mode Exit fullscreen mode

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.

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Postmark Image

"Please fix this..."

Focus on creating stellar experiences without email headaches. Postmark's reliable API and detailed analytics make your transactional emails as polished as your product.

Start free

Join the Runner H "AI Agent Prompting" Challenge: $10,000 in Prizes for 20 Winners!

Runner H is the AI agent you can delegate all your boring and repetitive tasks to - an autonomous agent that can use any tools you give it and complete full tasks from a single prompt.

Check out the challenge

DEV is bringing live events to the community. Dismiss if you're not interested. ❤️