So you're ready for green belt?

So you're ready for green belt?

Gopher with a green belt

It’s not easy being green, but this guide will help you.

Do you have the Go chops to earn a coveted green belt from BIT? Use this handy reference to check your knowledge and skills, to see if you’re at the required level.

This page is mostly intended for BIT students preparing for the Software Engineer - Intermediate certificate, but everybody’s welcome to use it as a checklist to see where they are in their learning journey.

What job am I ready for?

Roughly, a green belt suggests you’re ready to to join any commercial Go team as a full-time developer, and get straight down to work with minimal supervision. (A blue belt would qualify you to lead that team, while a brown belt would suggest you’re ready to lead the company. But one step at a time.)

What are the prerequisites?

You’ll need to have already obtained your yellow belt, which is the introductory qualification. Even at that level, you’ll be comfortable with writing Go programs and packages, test-first, and you’ll be familiar, if not expert, with the basic concepts of Go: structs, slices, loops, and functions.

If you’ve read and completed all the challenges in the For the Love of Go book, you can consider yourself the equivalent of a yellow belt.

How much experience should I have?

It doesn’t really matter how long you’ve been using Go; what’s important is how much you’ve learned in that time. Some pepole reach green belt standard in a few weeks, others in a few months. Some may never reach it, but anyone who’s determined, ready to learn, and prepared to put in significant time and effort will be able to achieve it.

Of course access to professional mentoring will speed up your progress, but short of that, you can always ask a more experienced colleague or friend to help you out. Pair program with them; ask them to review your code with you (this is much better done in person or via screen-sharing than through pull requests).

Read their programs and try to understand everything that you read. First, try to figure it out by yourself. Make a list of the questions you weren’t able to answer, and ask your friend about them. If, between you, you identify some important areas of knowledge that you need to work on, ask their advice about what you should do and how you should study them.

What should I know?

At green belt level, you should be thoroughly familiar with every aspect of the Go language, including generics, but not including the concurrency primitives (perhaps you’ll know something about these, but at any rate you won’t be tested on them).

In particular, you will need to have grasped the key concepts of interfaces, understand the uses of io.Reader and io.Writer, and be able to implement and use a simple interface in your own code. You should also be able to explain interfaces to someone who’s new to Go: what are they, why do we need them, how do we use them? When are they not appropriate? What is the empty interface, where and how should we use it, and how should we not use it?

You should be fully conversant with the Go spec. That’s not to say that you should have memorised everything in it, or be able to use every concept it covers, but you should understand what it says, and be able to use the spec document to answer any Go-related question.

The best answers will show not only that you can reproduce the plain facts of the spec, but that you have a broader understanding of the concepts and issues involved.

For example, you can expect to be asked questions such as the following:

  1. What is the universe block? Name some things you would find in it.

Model answer:

The block in Go is a unit of scope. For example, an if or for statement creates an implicit block, within which variables can be declared that are local to the block. The enclosing function is a higher-level block, as is the enclosing package. All user code is within some package or file block, but ultimately these are all contained within a top-level scope not available to users, called the universe block. All Go source code is inside the universe block.

A number of identifiers are declared within the universe block, including the built-in types (such as int and any), useful constants (true, false, and iota), the nil value, and Go’s built-in functions (for example append and len).

  1. Describe the syntax, behaviour, and purpose of a type switch. Give an example.

Model answer:

There are two types of switch statement in Go: expression switches and type switches. While an expression switch chooses different cases based on the value of a given expression, a type switch chooses different cases based on the dynamic type of a given interface value.

A type switch statement has the form:

switch x.(type) {
case A:
    // ...
case B:
    // ...
default:
    // ...
}

Optionally, it can include a short variable declaration, of the form:

switch i := x.(type) {
    // cases
}

For each possible type case, i will take the value of x asserted to that type.

Each case can specify a type, or list of types, including interface types and type parameters, but they must all be different. Exactly one nil case is also allowed, and this will be selected when x is nil (that is, when x has no dynamic type).

The purpose of a type switch is to take different actions depending on the dynamic type of x. For example, it could be used to print x in a different way depending whether x is an int, float64, and so on.

Example:

var x any
...
switch i := x.(type) {
case int:
    fmt.Printf("x is an integer: %d\n", i)
case string:
    fmt.Printf("x is a string: %q\n", i)
case nil:
    fmt.Println("x is nil")
default:
    fmt.Println("x is some other type")
}

The fallthrough keyword is not allowed in a type switch.

  1. What is an unsafe.Pointer? When would you use it? How would you use it? When is its use justified?

Model answer:

The pseudo-type unsafe.Pointer provides a way of bypassing the constraints of Go’s type system. An ordinary Go pointer is type-safe: that is, a *int cannot be converted to a *float64, for example. By contrast, once a *int is converted to an unsafe.Pointer, it can then be converted to any other pointer type. For example:

x := 12
p := unsafe.Pointer(&x)
y := (*float64)(p)
fmt.Printf("%T\n", y)
// *float64

Since any operation that requires an unsafe.Pointer is by definition unsafe, its legitimate uses are few, and rather specialised. It might be used, for example, to make very fast type conversions or to do pointer arithmetic, avoiding Go’s built-in safety checks because the safety is already assured by some other means.

Where interoperability with other type-worlds, such as Cgo or kernel syscalls, requires integer uintptr values, unsafe.Pointer can be used to convert ordinary Go pointers to this type. However, use of the unsafe package in general, and Pointer in particular, requires great care, and inevitably relies on implementation details which are not subject to the Go compatibility promise, and thus may change in a breaking way at any time.

  1. Explain the relationship between slices and their underlying arrays.
  2. What’s the difference between len on a string, and utf8.RuneCountInString? Why does it matter, and which should you use?
  3. Name as many as you can of the 15 built-in functions in Go, and briefly say what they do.
  4. What is the maximum value of a numeric constant in Go? What about a numeric variable?
  5. What is shadowing? When might it happen? How can you avoid it?
  6. Explain the difference between pointer and value methods, and say when each is appropriate, and why.
  7. What’s the difference between declaring a slice using var, and constructing one using make? Why does it matter? What aspects of your program might this choice affect?
  8. What is a type parameter? Where would you use one, and why? Outline the relationship between type parameters and type constraints.

If you feel confident about your answers to these questions, with access to the spec document, then you should have no trouble with the green belt test.

You should also have at least a basic understanding of modules and have a passing familiarity with the modules reference, though you won’t be expected to answer questions about it at this level. Nevertheless, you should know how to create and version a module, update its dependencies, and fix some simple dependency issues.

What should I be able to do?

You should be able to read any Go program fluently, accurately describing what the code says (concurrency excepted), and making intelligent deductions about what it means. (See the Code Club video series for some examples of this in action.)

You’ll also need to be able to write a variety of Go programs, such as command-line tools or library packages (the Let’s Code video series may help you). In particular you’ll be expected to know how to test some difficult functions, especially those which require dependency injection, without writing mocks (and you’ll understand why not).

You’ll be familiar with, and able to use in your programs, the most important functions and types from the most important standard library packages:

  • bytes and strings
  • encoding/json
  • errors
  • flag
  • fmt
  • net/http (both clients and servers)
  • io
  • os and os/exec
  • sort
  • testing
  • time

More importantly, you will be developing a good design sense, and be able to choose meaningful names for functions, identifiers, and so on. You’ll understand the concept of paperwork in APIs and know some techniques for eliminating or reducing it. You’ll be able to look at your code from a user’s point of view, spot awkward or inconvenient aspects of it, and improve them.

You’ll be able to quickly analyse any program, including your own, and find and apply simple refactorings to improve the readability and reliability of the code (for example, renaming a function or struct field). You’ll know the concepts of left-align the happy path and early return, and be able to apply them.

What other skills will I need?

You should have some experience of using the Git tools, the GitHub site, and working with branches and pull requests.

It’s also advisable to be extremely familiar with your editor of choice. It doesn’t matter which one you choose, and you won’t be tested on it, but when you’re operating at green belt level you need to be able to edit code fluently and fast, ideally using mostly keyboard shortcuts.

It’s worth taking some time to study your editor specifically, including the extensions you use. Specifically, you should be able to use the editor to:

  • “Go to definition”
  • “Rename symbol”
  • Highlight test coverage

This small investment will more than pay off over the next few years.

What should I have read?

Required:

Recommended:

What won’t I need to know?

Algorithms. You can look those up.

If you have a CS degree or background, great, but it doesn’t matter. You’ll be able to teach yourself everything you actually need that a CS course would have taught you, and it won’t take long: a few weeks of study, with access to the right books.

You should have a general understanding of the following concepts that really matter, and that will be valuable to you as a software engineer:

  • Basic computer architecture: CPU, memory, I/O (the G-machine project will help you)
  • Operating system fundamentals: kernel, processes, files, threads
  • Networking: TCP/IP, DNS, HTTP

You won’t need to be expert, or even particularly knowledgeable, about any of these things, just have a passing familiarity with them, and understand how they affect the programs you write.

How can I demonstrate my abilities?

If you’re studying at BIT, your instructor will have a good idea of your general skill level, and advise you of any gaps that you should work on. You’ll be tested on your spec knowledge and language understanding, but the most important part of your grading examination will be a Go project.

This should be some non-trivial module providing a library or CLI tool (ideally both). What it does isn’t as important as how it does it. The examiner will be looking for some basic requirements of any open-source Go project:

  • An open-source licence (for example, MIT)
  • A README with adequate instructions to import, install, and use the package
  • A semantic version release tag
  • User documentation on pkg.go.dev

Other things that will allow you to show off your developer skills include a sensible package and folder organisation within the module. Think about things like:

  • Does the module have a meaningful, yet concise name?
  • Does it have a sensible import path?
  • Does it minimise the number of non-standard imports?
  • Does it minimise the number of contained packages?
  • If there are internal packages, are they appropriate?
  • Does it export identifiers that should be exported, and keep unexported those that shouldn’t?
  • Is the API clear, purposeful, minimal, convenient, and helpful?

A model example of a well-presented Go project is:

What can I do to improve my code?

For your grading project, the examiner will be looking to see whether you can format your code correctly using gofmt (hopefully this is already set up in your editor), and eliminate unnecessary lint warnings from staticcheck. (If there are staticcheck warnings, you should be able to explain why they are false positives.)

Naturally, all tests should be passing, and since the tests are the most important part of your code, they should be clear, readable, well named and well organised. If your project is a CLI, it should have a friendly, polite, and convenient user interface, with good help text, and without excessive flags or options.

Does your program contain any commented-out code? Does it contain TODO comments? Are there code behaviours without sufficient test coverage? Does the code contain misspellings that you could have caught with a spell-checker? Is the code unnecessarily split into multiple files, or unrelated functions grouped together? Are there struct types that have come adrift from their methods? All these things will potentially lose you points.

Do all your public identifiers have appropriate doc comments? Have you checked the auto-generated documentation for your package on pkg.go.dev? Have you included executable examples where necessary? Refer to gotestdox as an example of a reasonably well-polished project.

Remember, you don’t need to produce perfect code; that’s impossible anyway. What your examiner will be looking for is clear, readable, and simple code that does what’s required. It doesn’t take brilliance to produce this, just diligence. If you work carefully, read over your code from time to time, and take the trouble to make it as clear as you can, you’ll do just fine.

Whether you’re in the BIT program or studying independently, you should try to get your project to the level of finish and polish where you’d feel comfortable putting it on your resume as the definitive statement of your software engineering skills. Then do so.

Testing CLI tools in Go

Testing CLI tools in Go

Test scripts in Go

Test scripts in Go

0