SEMVER AND MICROSERVICES
IN GO
by Viacheslav Poturaev
Golang meetup # 11
Agenda
• Microservices
• The problem
• Semantic versioning
• Version constraints
• Breaking changes in GO
• Dependency management in GO
• Glide
• Dep
Golang meetup # 11
Microservices
Big
PHP
Monolith
Nice
PHP
App
GO
API
GO
API
GO
API
GO
API
GO
API
GO
API
Team
Team
Team
TeamTeam
Team
Team
Team
TeamTeam
Team
Shared
libs and
clients
Golang meetup # 11
The	problem
Golang meetup # 11
MAJOR.MINOR.PATCH
1. MAJOR version when you make incompatible API changes,
2. MINOR version when you add functionality in a backwards-compatible manner, and
3. PATCH version when you make backwards-compatible bug fixes.
Semantic	Versioning
semver.org
X.0.0
• Breaking public changes
• MINOR and PATCH changes
X.Y.0
• New public features
• Deprecations
• PATCH changes
• New private features
• May be breaking for v0
X.Y.Z
• Non-breaking bug fixes
v1.0.0
1.0.0-alpha
1.0.0-rc1
2.1.5
v0.1.0
0.2.2-beta
1.0.0-beta+exp.sha.5114f85
v1.0.0-beta
Golang meetup # 11
Version	constraints
>= 1.2, < 3.0.0 || >= 4.2.3
Basic Comparisons
= equal (aliased to no operator)
!= not equal
> greater than
< less than
>= greater than or equal to
<= less than or equal to
Golang meetup # 11
Version	constraints
Hyphen Range Comparisons
1.2 - 1.4.5 which is equivalent to >= 1.2, <= 1.4.5
2.3.4 - 4.5 which is equivalent to >= 2.3.4, <= 4.5
Wildcards In Comparisons
1.2.* is equivalent to >= 1.2.0, < 1.3.0
>= 1.2.* is equivalent to >= 1.2.0
<= 2.* is equivalent to < 3
* is equivalent to >= 0.0.0
Golang meetup # 11
Version	constraints
Tilde Range Comparisons
~1.2.3 is equivalent to >= 1.2.3, < 1.3.0

~1 is equivalent to >= 1, < 2

~2.3 is equivalent to >= 2.3, < 2.4

~1.2.x is equivalent to >= 1.2.0, < 1.3.0

~1.x is equivalent to >= 1, < 2
Caret Range Comparisons (Major)
^1.2.3 is equivalent to >= 1.2.3, < 2.0.0

^1.2.x is equivalent to >= 1.2.0, < 2.0.0

^2.3 is equivalent to >= 2.3, < 3

^2.x is equivalent to >= 2.0.0, < 3

Golang meetup # 11
Semantic	Versioning
Application
Lib A
Lib B
Lib C
^1.0.0
2.0.0
master
1.2.4
1.2.3
1.2.2
2.1.0
2.0.0
1.0.0
Lib B
^2.0.0
^1.0.0
master
Golang meetup # 11
Breaking	changes	in	GO
• Any changes in exported interfaces
• Removal or change of method of non-exported interface (that is implicitly exported)
• Removal, rename or field tag change of exported property in exported struct
• New exported property in exported structure
• Removing func/type/var/const at package scope
• Changing a function/method signature
Non-breaking changes
• Adding func/type/var/const at package scope
Breaking changes
Golang meetup # 11
Breaking	changes	in	GO
package breaking // import "gitlab.com/vearutop/breaking"
import "errors"
// MyPublic is a publicly available struct
type MyPublic struct {
Param string `json:"param" db:"param"`
}
// DoWhatever does whatever it does
func (m MyPublic) DoWhatever(a MyPublic) (error, MyPublic) {
return Helper{"EN"}.GetError("ouch"), MyPublic{Param: a.Param}
}
// Helper is a imaginary friend
type Helper struct{ Lang string }
// GetError gets error
func (h Helper) GetError(text string) error { return errors.New(h.Lang + text) }
Golang meetup # 11
Breaking	changes	in	GO
package extensible // import "gitlab.com/vearutop/extensible"
import "gitlab.com/vearutop/extensible/internal"
type iSomething interface {
DoWhatever(self iSomething) (error, iSomething)
GetParam() string
}
// New creates something
func New(param string) iSomething { return &myPrivate{Param: param} }
type myPrivate struct{ Param string `json:"param" db:"param"` }
func (m myPrivate) DoWhatever(a iSomething) (error, iSomething) {
return internal.Helper{"EN"}.GetError("ouch"), New(a.GetParam())
}
func (m myPrivate) GetParam() string { return m.Param }
Golang meetup # 11
Breaking	changes	in	GO
package main
import (
"gitlab.com/vearutop/breaking"
"gitlab.com/vearutop/extensible"
)
type localEmbed struct {
Option int64
}
type local struct {
breaking.MyPublic
localEmbed
}
func (l localEmbed) DoAnything()
{ println("local") }
func main() {
a := extensible.New("Hi")
a.DoWhatever(a)
b := breaking.MyPublic{Param:
"Hi"}
b.DoWhatever(b)
c := local{}
c.Option = 123 // refers to
localEmbed
c.Param = "Hi"
c.DoWhatever(c.MyPublic)
c.DoAnything() // refers to
localEmbed
}
Golang meetup # 11
Breaking	changes	in	GO
Apicompat tool
HEAD:poor.go:15: breaking change declaration removed
type Helper struct{ Lang string }
HEAD:poor.go:18: breaking change declaration removed
func (h Helper) GetError(text string) error
poor.go:6: breaking change members changed types
type MyPublic struct {
Param string `json:"param" db:"param"`
}
type MyPublic struct {
Param int64 `json:"param" db:"param"`
}
https://github.com/bradleyfalzon/apicompat
Golang meetup # 11
Breaking	changes	in	GO
Safekeeping tips
• Don’t embed external structures and interfaces to avoid future collisions
• Avoid unkeyed struct literals for external structures
• Beware of reflect.DeepEqual against external structures
• Keep non-public exportables in internal folder
• Hide your public structs with constructors and interfaces
Golang meetup # 11
Dependency	management	in	GO
Go1
• Versioning by import path
• Imports with FQDN
• go get retrieves latest go1 or master reference
• Hard to make reproducible builds
• Import path supposed to not have breaking
changes after publishing
• Historically repo-level versioning was out of the
business
• Deps are globally stored under $GOPATH
The future
• Semantic versioning by release tags
• Imports with FQDN
• Dep requirements are defined in manifest file
by version constraints
• Build state is saved with lock file for
reproducible builds
• Fork-friendly
• Import path is not changed with breaking
change
• Deps are locally stored in local ./vendor
Golang meetup # 11
Glide
package: microfoorvice
ignore:
- github.com/skip/this/import
import:
- package: github.com/valuable/library
version: ^5.0.0
subpackages:
- config
- handler
- package: github.com/vektra/mockery/cmd/mockery
repo: https://github.com/codeactual/mockery.git
vcs: git
- package: github.com/tinylib/msgp
vcs: git
repo: ssh://git@gitlab.com/customforks/msgpack.git
version: ^1.0.0
subpackages:
- msgp
- parse
- printer
- gen
testImport:
- package: gopkg.in/check.v1
- package: github.com/tebeka/go2xunit
- package: github.com/alecthomas/gocyclo
Community-driven feature-rich tool
glide.yaml stores dev-defined constraints
glide.lock keeps snapshot of resolved constraints
glide install syncs vendor according to lock file
glide update resolves constraints with latest
available versions and saves to lock file
https://github.com/Masterminds/glide
v0.12.3
Golang meetup # 11
Dep
[[dependencies]]
name = "github.com/mailru/easyjson"
source = “gitlab.com/my-forks/easyjson”
version = "^1.0.0"
[[dependencies]]
name = “github.com/valuable/library“
version = "^2.0.0"
[[dependencies]]
name = "gopkg.in/urfave/cli.v2"
revision =
"1b5ad735df034545a2ce018d348a814d406fc258"
A work in progress official tool
Gopkg.toml keeps user intent
Gopkg.lock keeps snapshot of resolved revisions
dep init set up a new project
dep ensure install the project's dependencies
dep ensure -update update the locked versions
of all dependencies
dep ensure github.com/mailru/easyjson:gitlab.com/my-forks/easyjson@^1.0.0
pre-alpha
https://github.com/golang/dep/
Golang meetup # 11
Thanks!	Questions?

Semantic versioning and microservices in GO

  • 1.
    SEMVER AND MICROSERVICES INGO by Viacheslav Poturaev
  • 2.
    Golang meetup #11 Agenda • Microservices • The problem • Semantic versioning • Version constraints • Breaking changes in GO • Dependency management in GO • Glide • Dep
  • 3.
    Golang meetup #11 Microservices Big PHP Monolith Nice PHP App GO API GO API GO API GO API GO API GO API Team Team Team TeamTeam Team Team Team TeamTeam Team Shared libs and clients
  • 4.
    Golang meetup #11 The problem
  • 5.
    Golang meetup #11 MAJOR.MINOR.PATCH 1. MAJOR version when you make incompatible API changes, 2. MINOR version when you add functionality in a backwards-compatible manner, and 3. PATCH version when you make backwards-compatible bug fixes. Semantic Versioning semver.org X.0.0 • Breaking public changes • MINOR and PATCH changes X.Y.0 • New public features • Deprecations • PATCH changes • New private features • May be breaking for v0 X.Y.Z • Non-breaking bug fixes v1.0.0 1.0.0-alpha 1.0.0-rc1 2.1.5 v0.1.0 0.2.2-beta 1.0.0-beta+exp.sha.5114f85 v1.0.0-beta
  • 6.
    Golang meetup #11 Version constraints >= 1.2, < 3.0.0 || >= 4.2.3 Basic Comparisons = equal (aliased to no operator) != not equal > greater than < less than >= greater than or equal to <= less than or equal to
  • 7.
    Golang meetup #11 Version constraints Hyphen Range Comparisons 1.2 - 1.4.5 which is equivalent to >= 1.2, <= 1.4.5 2.3.4 - 4.5 which is equivalent to >= 2.3.4, <= 4.5 Wildcards In Comparisons 1.2.* is equivalent to >= 1.2.0, < 1.3.0 >= 1.2.* is equivalent to >= 1.2.0 <= 2.* is equivalent to < 3 * is equivalent to >= 0.0.0
  • 8.
    Golang meetup #11 Version constraints Tilde Range Comparisons ~1.2.3 is equivalent to >= 1.2.3, < 1.3.0
 ~1 is equivalent to >= 1, < 2
 ~2.3 is equivalent to >= 2.3, < 2.4
 ~1.2.x is equivalent to >= 1.2.0, < 1.3.0
 ~1.x is equivalent to >= 1, < 2 Caret Range Comparisons (Major) ^1.2.3 is equivalent to >= 1.2.3, < 2.0.0
 ^1.2.x is equivalent to >= 1.2.0, < 2.0.0
 ^2.3 is equivalent to >= 2.3, < 3
 ^2.x is equivalent to >= 2.0.0, < 3

  • 9.
    Golang meetup #11 Semantic Versioning Application Lib A Lib B Lib C ^1.0.0 2.0.0 master 1.2.4 1.2.3 1.2.2 2.1.0 2.0.0 1.0.0 Lib B ^2.0.0 ^1.0.0 master
  • 10.
    Golang meetup #11 Breaking changes in GO • Any changes in exported interfaces • Removal or change of method of non-exported interface (that is implicitly exported) • Removal, rename or field tag change of exported property in exported struct • New exported property in exported structure • Removing func/type/var/const at package scope • Changing a function/method signature Non-breaking changes • Adding func/type/var/const at package scope Breaking changes
  • 11.
    Golang meetup #11 Breaking changes in GO package breaking // import "gitlab.com/vearutop/breaking" import "errors" // MyPublic is a publicly available struct type MyPublic struct { Param string `json:"param" db:"param"` } // DoWhatever does whatever it does func (m MyPublic) DoWhatever(a MyPublic) (error, MyPublic) { return Helper{"EN"}.GetError("ouch"), MyPublic{Param: a.Param} } // Helper is a imaginary friend type Helper struct{ Lang string } // GetError gets error func (h Helper) GetError(text string) error { return errors.New(h.Lang + text) }
  • 12.
    Golang meetup #11 Breaking changes in GO package extensible // import "gitlab.com/vearutop/extensible" import "gitlab.com/vearutop/extensible/internal" type iSomething interface { DoWhatever(self iSomething) (error, iSomething) GetParam() string } // New creates something func New(param string) iSomething { return &myPrivate{Param: param} } type myPrivate struct{ Param string `json:"param" db:"param"` } func (m myPrivate) DoWhatever(a iSomething) (error, iSomething) { return internal.Helper{"EN"}.GetError("ouch"), New(a.GetParam()) } func (m myPrivate) GetParam() string { return m.Param }
  • 13.
    Golang meetup #11 Breaking changes in GO package main import ( "gitlab.com/vearutop/breaking" "gitlab.com/vearutop/extensible" ) type localEmbed struct { Option int64 } type local struct { breaking.MyPublic localEmbed } func (l localEmbed) DoAnything() { println("local") } func main() { a := extensible.New("Hi") a.DoWhatever(a) b := breaking.MyPublic{Param: "Hi"} b.DoWhatever(b) c := local{} c.Option = 123 // refers to localEmbed c.Param = "Hi" c.DoWhatever(c.MyPublic) c.DoAnything() // refers to localEmbed }
  • 14.
    Golang meetup #11 Breaking changes in GO Apicompat tool HEAD:poor.go:15: breaking change declaration removed type Helper struct{ Lang string } HEAD:poor.go:18: breaking change declaration removed func (h Helper) GetError(text string) error poor.go:6: breaking change members changed types type MyPublic struct { Param string `json:"param" db:"param"` } type MyPublic struct { Param int64 `json:"param" db:"param"` } https://github.com/bradleyfalzon/apicompat
  • 15.
    Golang meetup #11 Breaking changes in GO Safekeeping tips • Don’t embed external structures and interfaces to avoid future collisions • Avoid unkeyed struct literals for external structures • Beware of reflect.DeepEqual against external structures • Keep non-public exportables in internal folder • Hide your public structs with constructors and interfaces
  • 16.
    Golang meetup #11 Dependency management in GO Go1 • Versioning by import path • Imports with FQDN • go get retrieves latest go1 or master reference • Hard to make reproducible builds • Import path supposed to not have breaking changes after publishing • Historically repo-level versioning was out of the business • Deps are globally stored under $GOPATH The future • Semantic versioning by release tags • Imports with FQDN • Dep requirements are defined in manifest file by version constraints • Build state is saved with lock file for reproducible builds • Fork-friendly • Import path is not changed with breaking change • Deps are locally stored in local ./vendor
  • 17.
    Golang meetup #11 Glide package: microfoorvice ignore: - github.com/skip/this/import import: - package: github.com/valuable/library version: ^5.0.0 subpackages: - config - handler - package: github.com/vektra/mockery/cmd/mockery repo: https://github.com/codeactual/mockery.git vcs: git - package: github.com/tinylib/msgp vcs: git repo: ssh://git@gitlab.com/customforks/msgpack.git version: ^1.0.0 subpackages: - msgp - parse - printer - gen testImport: - package: gopkg.in/check.v1 - package: github.com/tebeka/go2xunit - package: github.com/alecthomas/gocyclo Community-driven feature-rich tool glide.yaml stores dev-defined constraints glide.lock keeps snapshot of resolved constraints glide install syncs vendor according to lock file glide update resolves constraints with latest available versions and saves to lock file https://github.com/Masterminds/glide v0.12.3
  • 18.
    Golang meetup #11 Dep [[dependencies]] name = "github.com/mailru/easyjson" source = “gitlab.com/my-forks/easyjson” version = "^1.0.0" [[dependencies]] name = “github.com/valuable/library“ version = "^2.0.0" [[dependencies]] name = "gopkg.in/urfave/cli.v2" revision = "1b5ad735df034545a2ce018d348a814d406fc258" A work in progress official tool Gopkg.toml keeps user intent Gopkg.lock keeps snapshot of resolved revisions dep init set up a new project dep ensure install the project's dependencies dep ensure -update update the locked versions of all dependencies dep ensure github.com/mailru/easyjson:gitlab.com/my-forks/easyjson@^1.0.0 pre-alpha https://github.com/golang/dep/
  • 19.
    Golang meetup #11 Thanks! Questions?