splash image

April 3, 2021

Getting started with Go

The Go programming language has become an important tool for developers, particularly around platforms like Kubernetes and Docker.

Go was created at Google by a team whose roots go back to Bell Labs and C. Their motivations included fast compilation time, and productive development of large scale distributed systems, handling high volumes of concurrent requests.

This article describes my experience as a new user of Go, building my first Go library. It follows a learning pattern similar to forays from Node to Rust.

Getting started

The Tour of Go is a great way to get familiar with the language syntax. I started with 'hello world' at golang.org, and found myself going back to the tour for different topics.

Screenshot of the Go Tour showing code and navigation

The macOS installer copies everything into /usr/local/go, so I opted to download the latest release from https://golang.org/dl/ into a versioned $GOROOT under my home directory. Here's what I have in my '.bash_profile':

export GOPATH=~/go
export GOROOT=~/go1.16.3
export PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin

VS Code

The VS Code Go extension has improved a lot over the years. It now auto-installs the delve debugger, and the gopls language server. I did not have to do any additional configuration.

Hovering over types like Builder shows source docs, and links to pkg.go.dev.

VS Code screenshot showing hover over strings.Builder

Porting from Rust to Go

I found it quite easy to port shortscale-rs to shortscale-go.

Go has no ownership syntax, and the run-time includes a garbage collector.

In this case, I was also lucky that the Go strings.Builder standard library package is very similar to the writer pattern I ended up using for Rust.

Overall, I was pleasantly surprised with the readability of the code

package shortscale

import (
  "strings"
)

// Shortscale converts numbers into English words.
// Supports positive integers from 0 to 999_999_999_999_999_999.
// Larger values return "(big number)".
func Shortscale(n uint64) string {
  if n <= 20 {
    return numwords[n]
  }
  if n > 999_999_999_999_999_999 {
    return "(big number)"
  }
  b := new(strings.Builder)
  writeScale(b, n, 1_000_000_000_000_000) // quadrillions
  writeScale(b, n, 1_000_000_000_000)     // trillions
  writeScale(b, n, 1_000_000_000)         // billions
  writeScale(b, n, 1_000_000)             // millions
  writeScale(b, n, 1_000)                 // thousands
  writeHundreds(b, n)
  writeTensAndUnits(b, n, b.Len() > 0)
  return b.String()
}

func writeTensAndUnits(b *strings.Builder, n uint64, ifAnd bool) {
  n = n % 100
  if n == 0 {
    return
  }
  if ifAnd {
    writeWord(b, "and")
  }
  if n <= 20 {
    writeWord(b, numwords[n])
    return
  }
  writeWord(b, numwords[n/10*10]) // tens
  units := n % 10
  if units > 0 {
    writeWord(b, numwords[units])
  }
}

func writeHundreds(b *strings.Builder, n uint64) {
  n = n / 100 % 10
  if n == 0 {
    return
  }
  writeWord(b, numwords[n])
  writeWord(b, numwords[100])
}

func writeScale(b *strings.Builder, n uint64, thousands uint64) {
  n = n / thousands % 1_000
  if n == 0 {
    return
  }
  writeHundreds(b, n)
  writeTensAndUnits(b, n, (n/100%10) > 0)
  writeWord(b, numwords[thousands])
}

func writeWord(b *strings.Builder, word string) {
  if b.Len() > 0 {
    b.WriteString(" ")
  }
  b.WriteString(word)
}

var numwords = map[uint64]string{
  0:                     "zero",
  1:                     "one",
  2:                     "two",
  3:                     "three",
  4:                     "four",
  5:                     "five",
  6:                     "six",
  7:                     "seven",
  8:                     "eight",
  9:                     "nine",
  10:                    "ten",
  11:                    "eleven",
  12:                    "twelve",
  13:                    "thirteen",
  14:                    "fourteen",
  15:                    "fifteen",
  16:                    "sixteen",
  17:                    "seventeen",
  18:                    "eighteen",
  19:                    "nineteen",
  20:                    "twenty",
  30:                    "thirty",
  40:                    "forty",
  50:                    "fifty",
  60:                    "sixty",
  70:                    "seventy",
  80:                    "eighty",
  90:                    "ninety",
  100:                   "hundred",
  1_000:                 "thousand",
  1_000_000:             "million",
  1_000_000_000:         "billion",
  1_000_000_000_000:     "trillion",
  1_000_000_000_000_000: "quadrillion",
}

Tests and benchmarks

The testing package provides support for running tests and benchmarks with go test. The GitHub Action workflow for shortscale-go make use of this.

Out of curiosity, I ran BenchmarkShortscale for two variants of the Shortscale function, one which pre-allocates memory for string.Builder, and one which does not. Pre-allocating, reduced the number of allocs/op from 4 to 1, improving ns/op by about 85ns.

Pre-allocated

$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/jldec/shortscale-go
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz

5694252	       205.5 ns/op	      64 B/op	       1 allocs/op

Not pre-allocated

$ go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/jldec/shortscale-go
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz

4100697	       292.9 ns/op	     120 B/op	       4 allocs/op

Dependency management

Until quite recently, Go did not have built-in package versioning like npm or cargo. This led to incompatibile versioning add-ons, like godep and glide, which made packages with nested dependencies difficult to consume. E.g. see this old INSTALL.md from kubernetes/client-go.

Fortunately, Go modules are enabled as the default in Go since v1.16.

go.mod

I created my shortscale-go module with go mod init following the guide in Using Go Modules.

$ go mod init github.com/jldec/shortscale-go
go: creating new go.mod: module github.com/jldec/shortscale-go

This created a new go.mod file with the following content.

module github.com/jldec/shortscale-go

go 1.16

I was a little surprised that there was no way to indicate the module version inside go.mod. Go relies on git tags in the form vx.x.x for this. As I pushed each version to GitHub, I used the GitHub Releases UI to create the tag.

pkg.go.dev

The shortscale package is published at https://pkg.go.dev/github.com/jldec/shortscale-go

Module page for shortscale-go on go.dev

It turns out that fetching any versioned module with go get, automatically adds that module to the registry at go.dev. This feels a little strange at first, but the more I use it, the more I think it's a clever solution.

How about using a similar scheme to create a vendor-neutral registry for ESM modules?
šŸ¤”

To leave a comment
please visit dev.to/jldec

debug

user: anonymous

{
  "path": "/blog/getting-started-with-go",
  "attrs": {
    "title": "Getting started with Go",
    "splash": {
      "image": "/images/church-blossoms.jpg"
    },
    "date": "2021-04-03",
    "layout": "BlogPostLayout",
    "excerpt": "The [Go](https://go.dev/) programming language has become an important tool for developers, particularly around platforms like Kubernetes and Docker.\n"
  },
  "md": "# Getting started with Go\n\nThe [Go](https://go.dev/) programming language has become an important tool for developers, particularly around platforms like Kubernetes and Docker.\n\nGo was created at Google by a team whose roots go back to Bell Labs and [C](https://en.wikipedia.org/wiki/C_(programming_language)). Their [motivations](https://talks.golang.org/2012/splash.article) included fast compilation time, and productive development of large scale distributed systems, handling high volumes of concurrent requests.\n\nThis article describes my experience as a new user of Go, building my first Go library. It follows a learning pattern similar to [forays from Node to Rust](forays-from-node-to-rust).\n\n## Getting started\n\nThe [Tour of Go](https://tour.golang.org/basics/1) is a great way to get familiar with the language syntax. I started with 'hello world' at [golang.org](https://golang.org/doc/tutorial/getting-started#install), and found myself going back to the tour for different topics.\n\n[![Screenshot of the Go Tour showing code and navigation](/images/go-tour.png \".no-border\")](https://tour.golang.org/basics/1)\n\nThe macOS [installer](https://golang.org/doc/manage-install) copies everything into `/usr/local/go`, so I opted to download the latest release from https://golang.org/dl/ into a versioned [$GOROOT](https://golang.org/doc/install/source#environment) under my home directory. Here's what I have in my '.bash_profile':\n\n```sh\nexport GOPATH=~/go\nexport GOROOT=~/go1.16.3\nexport PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin\n```\n\n## VS Code\n\nThe [VS Code Go](https://github.com/golang/vscode-go/) extension has improved a lot over the years. It now auto-installs the [delve](https://github.com/go-delve/delve) debugger, and the [gopls](https://blog.golang.org/gopls-vscode-go) language server. I did not have to do any additional configuration.\n\nHovering over types like `Builder` shows source docs, and links to [pkg.go.dev](https://go.dev).\n\n![VS Code screenshot showing hover over strings.Builder](/images/go-vs-code.png)\n\n## Porting from Rust to Go\n\nI found it quite easy to port [shortscale-rs](https://github.com/jldec/shortscale-rs/blob/main/src/shortscale.rs#L15) to [shortscale-go](https://github.com/jldec/shortscale-go/blob/main/shortscale.go).\n\nGo has no ownership syntax, and the run-time includes a garbage collector.\n\nIn this case, I was also lucky that the Go [strings.Builder](https://pkg.go.dev/strings#Builder) standard library package is very similar to the [writer](https://github.com/jldec/shortscale-rs/blob/main/src/shortscale.rs#L46) pattern I ended up using for Rust.\n\n> Overall, I was pleasantly surprised with the readability of the code\n\n```go\npackage shortscale\n\nimport (\n  \"strings\"\n)\n\n// Shortscale converts numbers into English words.\n// Supports positive integers from 0 to 999_999_999_999_999_999.\n// Larger values return \"(big number)\".\nfunc Shortscale(n uint64) string {\n  if n <= 20 {\n    return numwords[n]\n  }\n  if n > 999_999_999_999_999_999 {\n    return \"(big number)\"\n  }\n  b := new(strings.Builder)\n  writeScale(b, n, 1_000_000_000_000_000) // quadrillions\n  writeScale(b, n, 1_000_000_000_000)     // trillions\n  writeScale(b, n, 1_000_000_000)         // billions\n  writeScale(b, n, 1_000_000)             // millions\n  writeScale(b, n, 1_000)                 // thousands\n  writeHundreds(b, n)\n  writeTensAndUnits(b, n, b.Len() > 0)\n  return b.String()\n}\n\nfunc writeTensAndUnits(b *strings.Builder, n uint64, ifAnd bool) {\n  n = n % 100\n  if n == 0 {\n    return\n  }\n  if ifAnd {\n    writeWord(b, \"and\")\n  }\n  if n <= 20 {\n    writeWord(b, numwords[n])\n    return\n  }\n  writeWord(b, numwords[n/10*10]) // tens\n  units := n % 10\n  if units > 0 {\n    writeWord(b, numwords[units])\n  }\n}\n\nfunc writeHundreds(b *strings.Builder, n uint64) {\n  n = n / 100 % 10\n  if n == 0 {\n    return\n  }\n  writeWord(b, numwords[n])\n  writeWord(b, numwords[100])\n}\n\nfunc writeScale(b *strings.Builder, n uint64, thousands uint64) {\n  n = n / thousands % 1_000\n  if n == 0 {\n    return\n  }\n  writeHundreds(b, n)\n  writeTensAndUnits(b, n, (n/100%10) > 0)\n  writeWord(b, numwords[thousands])\n}\n\nfunc writeWord(b *strings.Builder, word string) {\n  if b.Len() > 0 {\n    b.WriteString(\" \")\n  }\n  b.WriteString(word)\n}\n\nvar numwords = map[uint64]string{\n  0:                     \"zero\",\n  1:                     \"one\",\n  2:                     \"two\",\n  3:                     \"three\",\n  4:                     \"four\",\n  5:                     \"five\",\n  6:                     \"six\",\n  7:                     \"seven\",\n  8:                     \"eight\",\n  9:                     \"nine\",\n  10:                    \"ten\",\n  11:                    \"eleven\",\n  12:                    \"twelve\",\n  13:                    \"thirteen\",\n  14:                    \"fourteen\",\n  15:                    \"fifteen\",\n  16:                    \"sixteen\",\n  17:                    \"seventeen\",\n  18:                    \"eighteen\",\n  19:                    \"nineteen\",\n  20:                    \"twenty\",\n  30:                    \"thirty\",\n  40:                    \"forty\",\n  50:                    \"fifty\",\n  60:                    \"sixty\",\n  70:                    \"seventy\",\n  80:                    \"eighty\",\n  90:                    \"ninety\",\n  100:                   \"hundred\",\n  1_000:                 \"thousand\",\n  1_000_000:             \"million\",\n  1_000_000_000:         \"billion\",\n  1_000_000_000_000:     \"trillion\",\n  1_000_000_000_000_000: \"quadrillion\",\n}\n```\n\n## Tests and benchmarks\n\nThe [testing](https://pkg.go.dev/testing) package provides support for running tests and benchmarks with `go test`. The GitHub Action [workflow](https://github.com/jldec/shortscale-go/blob/main/.github/workflows/ci.yaml#L25) for shortscale-go make use of this.\n\nOut of curiosity, I ran [BenchmarkShortscale](https://github.com/jldec/shortscale-go/blob/main/shortscale_test.go#L24) for two variants of the Shortscale function, one which [pre-allocates](https://github.com/jldec/shortscale-go/blob/358a49f24dcb9d4b2c697233f37f5dea4c87d318/shortscale.go#L18) memory for string.Builder, and one which does not. Pre-allocating, reduced the number of allocs/op from 4 to 1, improving ns/op by about 85ns.\n\n**Pre-allocated**\n```\n$ go test -bench . -benchmem\ngoos: darwin\ngoarch: amd64\npkg: github.com/jldec/shortscale-go\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\n\n5694252\t       205.5 ns/op\t      64 B/op\t       1 allocs/op\n```\n\n**Not pre-allocated**\n```\n$ go test -bench . -benchmem\ngoos: darwin\ngoarch: amd64\npkg: github.com/jldec/shortscale-go\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\n\n4100697\t       292.9 ns/op\t     120 B/op\t       4 allocs/op\n```\n\n\n## Dependency management\n\nUntil quite recently, Go [did not have](https://research.swtch.com/vgo-intro#versioning_and_api_stability) built-in package versioning like [npm](migrating-from-cjs-to-esm) or [cargo](forays-from-node-to-rust). This led to incompatibile versioning add-ons, like [godep](https://github.com/tools/godep) and [glide](https://github.com/Masterminds/glide), which made packages with nested dependencies difficult to consume. E.g. see this old [INSTALL.md](https://github.com/kubernetes/client-go/blob/416948da08dfd61cd4a08a3d679865ce91ff39b6/INSTALL.md#dependency-management-for-the-serious-or-reluctant-user) from kubernetes/client-go.\n\n> Fortunately, [Go modules](https://blog.golang.org/using-go-modules) are enabled as the default in Go since [v1.16](https://blog.golang.org/go116-module-changes).\n\n## go.mod\n\nI created my [shortscale-go](https://github.com/jldec/shortscale-go/blob/main/go.mod) module with [go mod init](https://golang.org/pkg/cmd/go/#hdr-Module_maintenance) following the guide in [Using Go Modules](https://blog.golang.org/using-go-modules).\n\n```sh\n$ go mod init github.com/jldec/shortscale-go\ngo: creating new go.mod: module github.com/jldec/shortscale-go\n```\nThis created a new **go.mod** file with the following content.\n\n```js\nmodule github.com/jldec/shortscale-go\n\ngo 1.16\n```\n\nI was a little surprised that there was no way to indicate the module version inside `go.mod`. Go [relies on git tags](https://blog.golang.org/publishing-go-modules) in the form vx.x.x for this. As I pushed each version to GitHub, I used the GitHub [Releases](https://github.com/jldec/shortscale-go/releases) UI to create the tag.\n\n## pkg.go.dev\n\nThe shortscale package is published at https://pkg.go.dev/github.com/jldec/shortscale-go\n\n[![Module page for shortscale-go on go.dev](/images/shortscale-go-go-dev.png)](https://pkg.go.dev/github.com/jldec/shortscale-go)\n\nIt turns out that fetching any versioned module with `go get`, automatically adds that module to the registry at [go.dev](https://go.dev/about). This feels a little strange at first, but the more I use it, the more I think it's a clever solution.\n\n> How about using a similar scheme to create a vendor-neutral registry for [ESM modules](extracting-an-esm-module-from-a-deno-script)?  \nšŸ¤”\n\n_To leave a comment  \nplease visit [dev.to/jldec](https://dev.to/jldec/getting-started-with-go-2m9e)_\n\n\n",
  "html": "<h1>Getting started with Go</h1>\n<p>The <a href=\"https://go.dev/\">Go</a> programming language has become an important tool for developers, particularly around platforms like Kubernetes and Docker.</p>\n<p>Go was created at Google by a team whose roots go back to Bell Labs and <a href=\"https://en.wikipedia.org/wiki/C_(programming_language)\">C</a>. Their <a href=\"https://talks.golang.org/2012/splash.article\">motivations</a> included fast compilation time, and productive development of large scale distributed systems, handling high volumes of concurrent requests.</p>\n<p>This article describes my experience as a new user of Go, building my first Go library. It follows a learning pattern similar to <a href=\"forays-from-node-to-rust\">forays from Node to Rust</a>.</p>\n<h2>Getting started</h2>\n<p>The <a href=\"https://tour.golang.org/basics/1\">Tour of Go</a> is a great way to get familiar with the language syntax. I started with 'hello world' at <a href=\"https://golang.org/doc/tutorial/getting-started#install\">golang.org</a>, and found myself going back to the tour for different topics.</p>\n<p><a href=\"https://tour.golang.org/basics/1\"><img src=\"/images/go-tour.png\" alt=\"Screenshot of the Go Tour showing code and navigation\" title=\".no-border\"></a></p>\n<p>The macOS <a href=\"https://golang.org/doc/manage-install\">installer</a> copies everything into <code>/usr/local/go</code>, so I opted to download the latest release from <a href=\"https://golang.org/dl/\">https://golang.org/dl/</a> into a versioned <a href=\"https://golang.org/doc/install/source#environment\">$GOROOT</a> under my home directory. Here's what I have in my '.bash_profile':</p>\n<pre><code class=\"language-sh\">export GOPATH=~/go\nexport GOROOT=~/go1.16.3\nexport PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin\n</code></pre>\n<h2>VS Code</h2>\n<p>The <a href=\"https://github.com/golang/vscode-go/\">VS Code Go</a> extension has improved a lot over the years. It now auto-installs the <a href=\"https://github.com/go-delve/delve\">delve</a> debugger, and the <a href=\"https://blog.golang.org/gopls-vscode-go\">gopls</a> language server. I did not have to do any additional configuration.</p>\n<p>Hovering over types like <code>Builder</code> shows source docs, and links to <a href=\"https://go.dev\">pkg.go.dev</a>.</p>\n<p><img src=\"/images/go-vs-code.png\" alt=\"VS Code screenshot showing hover over strings.Builder\"></p>\n<h2>Porting from Rust to Go</h2>\n<p>I found it quite easy to port <a href=\"https://github.com/jldec/shortscale-rs/blob/main/src/shortscale.rs#L15\">shortscale-rs</a> to <a href=\"https://github.com/jldec/shortscale-go/blob/main/shortscale.go\">shortscale-go</a>.</p>\n<p>Go has no ownership syntax, and the run-time includes a garbage collector.</p>\n<p>In this case, I was also lucky that the Go <a href=\"https://pkg.go.dev/strings#Builder\">strings.Builder</a> standard library package is very similar to the <a href=\"https://github.com/jldec/shortscale-rs/blob/main/src/shortscale.rs#L46\">writer</a> pattern I ended up using for Rust.</p>\n<blockquote>\n<p>Overall, I was pleasantly surprised with the readability of the code</p>\n</blockquote>\n<pre><code class=\"language-go\">package shortscale\n\nimport (\n  &quot;strings&quot;\n)\n\n// Shortscale converts numbers into English words.\n// Supports positive integers from 0 to 999_999_999_999_999_999.\n// Larger values return &quot;(big number)&quot;.\nfunc Shortscale(n uint64) string {\n  if n &lt;= 20 {\n    return numwords[n]\n  }\n  if n &gt; 999_999_999_999_999_999 {\n    return &quot;(big number)&quot;\n  }\n  b := new(strings.Builder)\n  writeScale(b, n, 1_000_000_000_000_000) // quadrillions\n  writeScale(b, n, 1_000_000_000_000)     // trillions\n  writeScale(b, n, 1_000_000_000)         // billions\n  writeScale(b, n, 1_000_000)             // millions\n  writeScale(b, n, 1_000)                 // thousands\n  writeHundreds(b, n)\n  writeTensAndUnits(b, n, b.Len() &gt; 0)\n  return b.String()\n}\n\nfunc writeTensAndUnits(b *strings.Builder, n uint64, ifAnd bool) {\n  n = n % 100\n  if n == 0 {\n    return\n  }\n  if ifAnd {\n    writeWord(b, &quot;and&quot;)\n  }\n  if n &lt;= 20 {\n    writeWord(b, numwords[n])\n    return\n  }\n  writeWord(b, numwords[n/10*10]) // tens\n  units := n % 10\n  if units &gt; 0 {\n    writeWord(b, numwords[units])\n  }\n}\n\nfunc writeHundreds(b *strings.Builder, n uint64) {\n  n = n / 100 % 10\n  if n == 0 {\n    return\n  }\n  writeWord(b, numwords[n])\n  writeWord(b, numwords[100])\n}\n\nfunc writeScale(b *strings.Builder, n uint64, thousands uint64) {\n  n = n / thousands % 1_000\n  if n == 0 {\n    return\n  }\n  writeHundreds(b, n)\n  writeTensAndUnits(b, n, (n/100%10) &gt; 0)\n  writeWord(b, numwords[thousands])\n}\n\nfunc writeWord(b *strings.Builder, word string) {\n  if b.Len() &gt; 0 {\n    b.WriteString(&quot; &quot;)\n  }\n  b.WriteString(word)\n}\n\nvar numwords = map[uint64]string{\n  0:                     &quot;zero&quot;,\n  1:                     &quot;one&quot;,\n  2:                     &quot;two&quot;,\n  3:                     &quot;three&quot;,\n  4:                     &quot;four&quot;,\n  5:                     &quot;five&quot;,\n  6:                     &quot;six&quot;,\n  7:                     &quot;seven&quot;,\n  8:                     &quot;eight&quot;,\n  9:                     &quot;nine&quot;,\n  10:                    &quot;ten&quot;,\n  11:                    &quot;eleven&quot;,\n  12:                    &quot;twelve&quot;,\n  13:                    &quot;thirteen&quot;,\n  14:                    &quot;fourteen&quot;,\n  15:                    &quot;fifteen&quot;,\n  16:                    &quot;sixteen&quot;,\n  17:                    &quot;seventeen&quot;,\n  18:                    &quot;eighteen&quot;,\n  19:                    &quot;nineteen&quot;,\n  20:                    &quot;twenty&quot;,\n  30:                    &quot;thirty&quot;,\n  40:                    &quot;forty&quot;,\n  50:                    &quot;fifty&quot;,\n  60:                    &quot;sixty&quot;,\n  70:                    &quot;seventy&quot;,\n  80:                    &quot;eighty&quot;,\n  90:                    &quot;ninety&quot;,\n  100:                   &quot;hundred&quot;,\n  1_000:                 &quot;thousand&quot;,\n  1_000_000:             &quot;million&quot;,\n  1_000_000_000:         &quot;billion&quot;,\n  1_000_000_000_000:     &quot;trillion&quot;,\n  1_000_000_000_000_000: &quot;quadrillion&quot;,\n}\n</code></pre>\n<h2>Tests and benchmarks</h2>\n<p>The <a href=\"https://pkg.go.dev/testing\">testing</a> package provides support for running tests and benchmarks with <code>go test</code>. The GitHub Action <a href=\"https://github.com/jldec/shortscale-go/blob/main/.github/workflows/ci.yaml#L25\">workflow</a> for shortscale-go make use of this.</p>\n<p>Out of curiosity, I ran <a href=\"https://github.com/jldec/shortscale-go/blob/main/shortscale_test.go#L24\">BenchmarkShortscale</a> for two variants of the Shortscale function, one which <a href=\"https://github.com/jldec/shortscale-go/blob/358a49f24dcb9d4b2c697233f37f5dea4c87d318/shortscale.go#L18\">pre-allocates</a> memory for string.Builder, and one which does not. Pre-allocating, reduced the number of allocs/op from 4 to 1, improving ns/op by about 85ns.</p>\n<p><strong>Pre-allocated</strong></p>\n<pre><code>$ go test -bench . -benchmem\ngoos: darwin\ngoarch: amd64\npkg: github.com/jldec/shortscale-go\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\n\n5694252\t       205.5 ns/op\t      64 B/op\t       1 allocs/op\n</code></pre>\n<p><strong>Not pre-allocated</strong></p>\n<pre><code>$ go test -bench . -benchmem\ngoos: darwin\ngoarch: amd64\npkg: github.com/jldec/shortscale-go\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\n\n4100697\t       292.9 ns/op\t     120 B/op\t       4 allocs/op\n</code></pre>\n<h2>Dependency management</h2>\n<p>Until quite recently, Go <a href=\"https://research.swtch.com/vgo-intro#versioning_and_api_stability\">did not have</a> built-in package versioning like <a href=\"migrating-from-cjs-to-esm\">npm</a> or <a href=\"forays-from-node-to-rust\">cargo</a>. This led to incompatibile versioning add-ons, like <a href=\"https://github.com/tools/godep\">godep</a> and <a href=\"https://github.com/Masterminds/glide\">glide</a>, which made packages with nested dependencies difficult to consume. E.g. see this old <a href=\"https://github.com/kubernetes/client-go/blob/416948da08dfd61cd4a08a3d679865ce91ff39b6/INSTALL.md#dependency-management-for-the-serious-or-reluctant-user\">INSTALL.md</a> from kubernetes/client-go.</p>\n<blockquote>\n<p>Fortunately, <a href=\"https://blog.golang.org/using-go-modules\">Go modules</a> are enabled as the default in Go since <a href=\"https://blog.golang.org/go116-module-changes\">v1.16</a>.</p>\n</blockquote>\n<h2>go.mod</h2>\n<p>I created my <a href=\"https://github.com/jldec/shortscale-go/blob/main/go.mod\">shortscale-go</a> module with <a href=\"https://golang.org/pkg/cmd/go/#hdr-Module_maintenance\">go mod init</a> following the guide in <a href=\"https://blog.golang.org/using-go-modules\">Using Go Modules</a>.</p>\n<pre><code class=\"language-sh\">$ go mod init github.com/jldec/shortscale-go\ngo: creating new go.mod: module github.com/jldec/shortscale-go\n</code></pre>\n<p>This created a new <strong>go.mod</strong> file with the following content.</p>\n<pre><code class=\"language-js\">module github.com/jldec/shortscale-go\n\ngo 1.16\n</code></pre>\n<p>I was a little surprised that there was no way to indicate the module version inside <code>go.mod</code>. Go <a href=\"https://blog.golang.org/publishing-go-modules\">relies on git tags</a> in the form vx.x.x for this. As I pushed each version to GitHub, I used the GitHub <a href=\"https://github.com/jldec/shortscale-go/releases\">Releases</a> UI to create the tag.</p>\n<h2>pkg.go.dev</h2>\n<p>The shortscale package is published at <a href=\"https://pkg.go.dev/github.com/jldec/shortscale-go\">https://pkg.go.dev/github.com/jldec/shortscale-go</a></p>\n<p><a href=\"https://pkg.go.dev/github.com/jldec/shortscale-go\"><img src=\"/images/shortscale-go-go-dev.png\" alt=\"Module page for shortscale-go on go.dev\"></a></p>\n<p>It turns out that fetching any versioned module with <code>go get</code>, automatically adds that module to the registry at <a href=\"https://go.dev/about\">go.dev</a>. This feels a little strange at first, but the more I use it, the more I think it's a clever solution.</p>\n<blockquote>\n<p>How about using a similar scheme to create a vendor-neutral registry for <a href=\"extracting-an-esm-module-from-a-deno-script\">ESM modules</a>?<br>\nšŸ¤”</p>\n</blockquote>\n<p><em>To leave a comment<br>\nplease visit <a href=\"https://dev.to/jldec/getting-started-with-go-2m9e\">dev.to/jldec</a></em></p>\n"
}