Blog article

Top 10 Go Tricks to Make Your Development Easier

Go (Golang) is famous for its simplicity, speed, and reliability.

readytools

July 1, 2026

3 min read

Top 10 Go Tricks to Make Your Development Easier

Image source: encrypted-tbn0.gstatic.com

Share

Go (Golang) is famous for its simplicity, speed, and reliability. But even with its clean syntax, there are plenty of practical tricks that can save you time, reduce bugs, and make your code more idiomatic. Here are 10 of the most useful Go tricks that seasoned developers use to write cleaner, faster, and more maintainable code

1. Use go run . and Build Tags for Quick Iteration

Forget typing long go run main.go commands. Just run:

BASH
go run .

For even more power, use build tags to toggle features during development:

GO
// +build debug
package main

Or with the modern syntax:

GO
//go:build debug

This lets you keep debug code without polluting production builds.

2. Embed Static Files with embed

Since Go 1.16, embedding files is built-in and incredibly useful for CLIs, web servers, or templates:

GO
import "embed"
//go:embed templates/*
var templates embed.FS
//go:embed static/*
var static embed.FS

No more external dependencies or complex asset pipelines. Perfect for single-binary deployments.

3. Functional Options Pattern for Flexible Constructors

Instead of massive config structs or many constructor variants, use the Functional Options pattern:

GO
type Server struct {
    addr string
    port int
    tls  bool
}
type Option func(*Server)
func WithTLS() Option { ... }
func NewServer(opts ...Option) *Server {
    s := &Server{addr: "localhost", port: 8080}
    for _, opt := range opts {
        opt(s)
    }
    return s
}

Clean, extensible, and very idiomatic.

4. context.Context Everywhere (and Proper Cancellation)

Always pass context.Context to functions that might be long-running or network-bound. Use context.WithTimeout or context.WithCancel liberally:

GO
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

This one habit prevents many goroutine leaks and makes your services more resilient.

5. Generics for Reducing Boilerplate (Go 1.18+)

Don’t fear generics. They shine for utilities like maps, sets, or data processing:

GO
func Map[T, U any](slice []T, fn func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = fn(v)
    }
    return result
}

Use them judiciously—don’t over-engineer, but they can eliminate a lot of copy-paste code.

6. sync.Once for Lazy Initialization

Need to initialize something exactly once across goroutines? sync.Once is perfect:

GO
var (
    once sync.Once
    db   *sql.DB
)
func getDB() *sql.DB {
    once.Do(func() {
        db = connectToDB()
    })
    return db
}

Simple, thread-safe, and cleaner than sync.Mutex + double-checked locking.

7. Error Wrapping with %w and errors.Is / errors.As

Always wrap errors for better debugging:

GO
return fmt.Errorf("failed to fetch user %d: %w", id, err)

Then check them properly:

GO
if errors.Is(err, sql.ErrNoRows) { ... }

This gives you rich error context without losing the original error type.

8. Use strings.Builder and bytes.Buffer Wisely

For performance-critical string building:

GO
var b strings.Builder
for _, item := range items {
    b.WriteString(item)
    b.WriteByte(',')
}
result := b.String()

Much more efficient than repeated += operations.

9. Table-Driven Tests and t.Run for Subtests

Level up your testing:

GO
func TestProcess(t *testing.T) {
    tests := []struct{
        name  string
        input string
        want  int
    }{ ... }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // test logic
        })
    }
}

This keeps your tests organized and makes failures easy to pinpoint.

10. go:generate for Code Generation

Automate repetitive tasks with //go:generate:

GO
//go:generate stringer -type=Status
//go:generate mockgen -source=repository.go -destination=mock.go

Combine it with tools like wire, mockgen, or custom generators to keep your code DRY.


Bonus Tip: Get comfortable with go vet, staticcheck, and golangci-lint. Running a good linter locally (and in CI) catches many issues before they become problems. Go rewards simplicity and explicitness. These tricks help you stay in that sweet spot: expressive code that’s still fast and easy to reason about.


Build faster with ReadyTools

Discover ReadyTools: the ultimate productivity suite for creators. Beautiful Linksy pages, smart Lara AI, project management, secure cloud storage, and everything else you need — all together. Start your 7-day free trial today.

Explore ReadyTools

Table of Contents

1. Use go run . and Build Tags for Quick Iteration2. Embed Static Files with embed3. Functional Options Pattern for Flexible Constructors4. context.Context Everywhere (and Proper Cancellation)5. Generics for Reducing Boilerplate (Go 1.18+)6. sync.Once for Lazy Initialization7. Error Wrapping with %w and errors.Is / errors.As8. Use strings.Builder and bytes.Buffer Wisely9. Table-Driven Tests and t.Run for Subtests10. go:generate for Code Generation

Keep reading

Related Posts

View all articles

Top tools

WorkspaceLinksyBoardlyChromoCodeHub

ReadyTools

CareersContactTools
Pricing7 days free
SupportGuidesDocsBlogUpdatesLaraVault

Select Language

Set theme

© 2026 ReadyTools. All rights reserved.