0% found this document useful (0 votes)
154 views

Write Go Like A Senior Engineer. What I Wish I Knew When I Started - by Jacob Bennett - Level Up Coding

The document discusses key things the author wishes they knew when starting to write Go code professionally in 2018. It covers how Go is pass-by-value, but pointers can be used to pass structs by reference. It also discusses using pointers carefully and avoiding nil pointer dereference errors by checking for nil pointers before calling methods.

Uploaded by

murthynsmpranu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
154 views

Write Go Like A Senior Engineer. What I Wish I Knew When I Started - by Jacob Bennett - Level Up Coding

The document discusses key things the author wishes they knew when starting to write Go code professionally in 2018. It covers how Go is pass-by-value, but pointers can be used to pass structs by reference. It also discusses using pointers carefully and avoiding nil pointer dereference errors by checking for nil pointers before calling methods.

Uploaded by

murthynsmpranu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Write Go like a senior engineer.

What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

Search Medium Write

You have 2 free member-only stories left this month. Upgrade for unlimited access.

Member-only story

Write Go like a senior engineer


What I wish I knew when I started writing Go

Jacob Bennett · Follow


Published in Level Up Coding · 7 min read · Dec 11, 2022

1K 8

Renee French, CC BY 3.0, via Wikimedia Commons

I started writing Go professionally in 2018. Here’s what I wish I could tell


myself when I started.

Go is pass-by-value
Go is exclusively pass-by-value. That means each function receives a copy of
the value of what you passed in. No exceptions.

Sort of.

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 1 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

You can pass pointers, too (you’re technically passing the value of the pointer
— the address). And with Go’s strong typing you’ll get type checking on the
underlying pointer.

In this example, we pass the rect struct by value. The struct goes into the
function and all operations modify the value that was passed in, but the
original object remains unchanged since it wasn’t passed by reference.

// https://go.dev/play/p/TAiWwsOZ_n6
package main

import "fmt"

type Rectangle struct {


Width int
Height int
}

func DoubleHeight(rect Rectangle) {


rect.Height = rect.Height * 2
}

func main() {
rect := Rectangle{
Width: 10,
Height: 3,
}

// this won't actually modify rect


DoubleHeight(rect)

fmt.Println("Width", rect.Width)
fmt.Println("Height", rect.Height)
}

This function doesn’t do what we want it to do. The object didn’t get updated.

But we can use a pointer to rect as the argument to the DoubleHeight

function and effectively pass rect by reference.

// https://go.dev/play/p/7L5QQtvzdDU
package main

import "fmt"

type Rectangle struct {


Width int
Height int
}

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 2 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

func DoubleHeight(rect *Rectangle) {


rect.Height = rect.Height * 2
}

func main() {
rect := Rectangle{
Width: 10,
Height: 3,
}

// this modifies rect


DoubleHeight(&rect)

fmt.Println("Width", rect.Width)
fmt.Println("Height", rect.Height)
}

Use (but don’t abuse) pointers


There are 2 pointer-related operators: * and &.

The * operator
The * operator is called the “pointer” operator. The type *Rectangle is a
pointer to a Rectangle instance. Declaring an argument of type *Rectangle

means the function accepts “a pointer to a Rectangle instance”.

var rect *Rectangle

The zero-value of a pointer is nil . Super useful in lots of cases! But it’ll often
lead to panic getting called when you try to call functions on a nil pointer.
Awful to debug (more on that later).

The & operator


The & operator (called the “address of” operator) generates a pointer to its
operand.

rect := Rectangle{
Width: 10,
Height: 3,
}
pntr := &rect

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 3 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

To get the pointer’s underlying value, use the * operator. This is called
“dereferencing the pointer.”

// read rect through the pointer


fmt.Println(*pntr)

// set rect through the pointer


*pntr = Rectangle{
Width: 20,
Height: 4,
}

If you’re ever wondering “should I use a pointer here?” the answer is


probably “Yes.” When in doubt, use a pointer.

nil pointer dereference errors


The bane of my existence.

Sometimes when you use pointers, you'll get this panic that completely shuts
down your program:

panic: runtime error: invalid memory address or nil pointer dereference

This was caused by you trying to dereference a pointer that was nil .

// https://go.dev/play/p/XjtkrEudRe9
package main

import "fmt"

type Rectangle struct {


Width int
Height int
}

func (r *Rectangle) Area() int {


return r.Width * r.Height
}

func main() {
var rect *Rectangle // default value is nil
fmt.Println(rect.Area()) // this will panic
}

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 4 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

The fix is easy. You can check for pointer values before you call methods on
them.

// https://go.dev/play/p/VM0fdzZjiq_Y
package main

import "fmt"

type Rectangle struct {


Width int
Height int
}

func (r *Rectangle) Area() int {


return r.Width * r.Height
}

func main() {
var rect *Rectangle

if rect != nil {
fmt.Println(rect.Area())
} else {
fmt.Println("rect is nil!")
}
}

These errors are often difficult to troubleshoot, and they’ve cost me hours of
bug hunting. If you decide to use pointers, always always check for nil values!

Declare interfaces where you use them


Yeah, ok. Pointers are pretty fundamental in Go. And they aren’t unique to
Go. But interfaces really screw over JS or Python engineers when they
switch.

Interfaces in Go are like a contract that specifies a set of methods that a type
must implement in order to fulfill the contract. For example, if you have an
interface called Reader that defines a Read() method, any type that
implements a Read() method is said to implement the Reader interface, and
can be used wherever a Reader is expected.

One of the cool things about interfaces in Go is that a type can implement
multiple interfaces. For example, if you have a type called File that

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 5 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

implements the Reader and Writer interfaces, you can use a File wherever
you need a Reader or a Writer , or even wherever you need something that
implements both interfaces. This makes it easy to write code that is flexible
and reusable.

Another cool thing about interfaces in Go is that you can use them to define
generic algorithms or functions that work with multiple types. For example,
you could write a sorting function that takes a sort.Interface as input, and
can sort any type that implements the required methods.

package sort

type Interface interface{


Len() int
Less (i , j) bool
Swap(i , j int)
}

This makes it easy to write code that is highly flexible and customizable. You
can create a custom type (e.g. a UserList or PostFeed ) and define those
methods and use them with the standard library.

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 6 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

The secret weapon


I’m not sure why Go doesn’t do this out-of-the-box. But there’s a lot it doesn’t
do out-of-the-box (e.g. error handling). I can only assume this is intentional
design.

Here’s the problem:

How can you guarantee that a struct complies with all of an interface’s
Top highlight
methods?

type SomeInterface interface {


Method()
}

type Implementation struct{}

func (*Implementation) Method() { fmt.Println("Hello, World!") }

var _ SomeInterface = (*Implementation)(nil) // ← this is the line

This last line ensures that Implementation satisfies all methods of


SomeInterface , and will fail to compile if SomeInterface adds methods that
Implementation fails to satisfy.

I’ve created a more full demo of this concept that you can play around with
at go.dev →

Prefer table tests, but don’t overdo it


When you test a function you are really just changing what the inputs and
expected outputs are. Go lets you do this in a really straightforward way
using table tests (or table-driven tests).

Let’s start with the code we want to test. It’s a bit of a mess, but it runs a
critical function in our system so we don’t want to touch it without knowing
that it does exactly what we expect.

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 7 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

package main

import "strings"

func ToSnakeCase(str string) string {


// step 0: handle empty string
if str == "" {
return ""
}

// step 1: CamelCase to snake_case


for i := 0; i < len(str); i++ {
if str[i] >= 'A' && str[i] <= 'Z' {
if i == 0 {
str = strings.ToLower(string(str[i])) + str[i+1:]
} else {
str = str[:i] + "_" + strings.ToLower(string(str[i])) + str[i+1:]
}
}
}

// step 2: remove spaces


str = strings.ReplaceAll(str, " ", "")

// step 3: remove double-underscores


str = strings.ReplaceAll(str, "__", "_")

return str
}

We probably want to test a wide range of inputs to make sure we are getting
the right outputs. Instead of writing individual tests for this, use table testing
to achieve the same result with a cleaner, easier-to-maintain result.

package main

import "testing"

func TestToSnakeCase(t *testing.T) {


type testCase struct {
description string
input string
expected string
}

testCases := []testCase{
{
description: "empty string",
input: "",
expected: "",
},
{
description: "single word",
input: "Hello",
expected: "hello",

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 8 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

},
{
description: "two words (camel case)",
input: "HelloWorld",
expected: "hello_world",
},
{
description: "two words with space",
input: "Hello World",
expected: "hello_world",
},
{
description: "two words with space and underscore",
input: "Hello_World",
expected: "hello_world",
},
}

for _, tc := range testCases {


t.Run(tc.description, func(t *testing.T) {
actual := ToSnakeCase(tc.input)
if actual != tc.expected {
t.Errorf("expected %s, got %s", tc.expected, actual)
}
})
}
}

These test cases are really nice to read!

When to avoid table tests


A good litmus test is: If you’re making logic branches in your actual test call,
you either shouldn’t be using table tests or your function is too complicated
and should be broken up.

This example isn’t too bad to follow (even though it isn’t great code) but it’s
still a code smell that may indicate a poorly-designed function.

// this is BAD
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
result, err := SomeOverlyComplexFunction(tc.input)

if err == nil {
if tc.expectedResult != result {
t.Errorf("expected %v, got %v", tc.expectedResult, result)
}
} else {
if !strings.Contains(err.Error(), tc.expectedErr.Error()) {
t.Errorf("expected error to be %s, got %s", tc.expectedErr.Error(), err.Error())
}
}
})

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 9 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

Other resources
Effective Go is the starting point for new Go engineers. It remains a constant
bookmark for me even after years of using the language.

Effective Go - The Go Programming Language


Go is a new language. Although it borrows ideas from existing
languages, it has unusual properties that make effective…
go.dev

Level Up Coding
Thanks for being a part of our community! Before you go:

Clap for the story and follow the author

View more content in the Level Up Coding publication

Follow us: Twitter | LinkedIn | Newsletter

Join the Level Up talent collective and find an amazing job

Programming Go Software Development

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 10 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

Written by Jacob Bennett Follow

8.1K Followers · Writer for Level Up Coding

Engineering, design, curiosity • Dev @ Medium • https://jacob.bio

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 11 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

More from Jacob Bennett and Level Up Coding

Jacob Bennett in Level Up Coding Julie Perilla Garcia in Level Up Coding

Use Git like a senior engineer To Be A Great Software Developer 


Git is a powerful tool that feels great to use —  You Need a System
when you know how to use it. Here’s how to build one.

· 4 min read · Nov 14, 2022 · 7 min read · Jun 22

7.4K 76 2.3K 30

Arslan Mirza in Level Up Coding Jacob Bennett in Geek Culture

The Dark Side of Software The 5 paid subscriptions I actually


Engineering use in 2023 as a software engineer
Why I Regret My Career Choice Tools I use that are cheaper than Netflix

· 10 min read · Jun 12 · 4 min read · Mar 25

1.1K 22 3.2K 33

See all from Jacob Bennett See all from Level Up Coding

Recommended from Medium


https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 12 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

Recommended from Medium

Matthias Bruns The Coding Diaries in The Coding Diaries

Golang — The Ultimate Guide to Why Experienced Programmers


Dependency Injection Fail Coding Interviews
Comparing manual and framework A friend of mine recently joined a FAANG
Dependency Injection company as an engineering manager, and
found themselves in the position of recruiting
· 17 min read · Mar 2 for…· 5 min read · Nov 1, 2022

131 2 109

Lists

General Coding Knowledge It's never too late or early to


20 stories · 70 saves start something
10 stories · 30 saves

Coding & Development Stories to Help You Grow as a


11 stories · 51 saves Software Developer
19 stories · 176 saves

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 13 of 14
Write Go like a senior engineer. What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM

Jacob Bennett in Level Up Coding Dr. Derek Austin in Better Programming

Use Git like a senior engineer Why I Prefer Regular Merge


Git is a powerful tool that feels great to use Commits Over Squash Commits
when you know how to use it. I used to think squash commits were so cool,
and then I had to use them all day, every day.
Here’s why you should avoid squash
· 4 min read · Nov 14, 2022 · 5 min read · Sep 30, 2022

7.4K 76 53

Love Shar… in ByteByteGo System Design Allian… Julie Perilla Garcia in Level Up Coding
a e
System Design Blueprint: The To Be A Great Software Developer 
Ultimate Guide —  You Need a System
Developing a robust, scalable, and efficient Here’s how to build one.
system can be daunting. However,
understanding the key concepts and
· 9 min readcan
components · Apr 20 the…
make · 7 min read · Jun 22

49 2.3K 30

See more recommendations

Help Status Writers Blog Careers Privacy Terms About Text to speech Teams

https://levelup.gitconnected.com/write-go-like-a-senior-engineer-eee7f03a1883 Page 14 of 14

You might also like