Write Go Like A Senior Engineer. What I Wish I Knew When I Started - by Jacob Bennett - Level Up Coding
Write Go Like A Senior Engineer. What I Wish I Knew When I Started - by Jacob Bennett - Level Up Coding
What I wish I knew when I started… | by Jacob Bennett | Level Up Coding 7/16/23, 8:42 AM
You have 2 free member-only stories left this month. Upgrade for unlimited access.
Member-only story
1K 8
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"
func main() {
rect := Rectangle{
Width: 10,
Height: 3,
}
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.
// https://go.dev/play/p/7L5QQtvzdDU
package main
import "fmt"
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 main() {
rect := Rectangle{
Width: 10,
Height: 3,
}
fmt.Println("Width", rect.Width)
fmt.Println("Height", rect.Height)
}
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
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).
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.”
Sometimes when you use pointers, you'll get this panic that completely shuts
down your program:
This was caused by you trying to dereference a pointer that was nil .
// https://go.dev/play/p/XjtkrEudRe9
package main
import "fmt"
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"
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!
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
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
How can you guarantee that a struct complies with all of an interface’s
Top highlight
methods?
I’ve created a more full demo of this concept that you can play around with
at go.dev →
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"
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"
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",
},
}
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.
Level Up Coding
Thanks for being a part of our community! Before you go:
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
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
7.4K 76 2.3K 30
1.1K 22 3.2K 33
See all from Jacob Bennett See all from Level Up Coding
131 2 109
Lists
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
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
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