Slide 1

Slide 1 text

© 2024 Thoughtworks | Confidential Why Go? A presentation originally for Docebo Then for the Milano-XPUG

Slide 2

Slide 2 text

© 2024 Thoughtworks | Confidential Software engineering at Google 2 “Software engineeringˮ is what happens to programming when you add time and other programmers Rob Pike "Go at Google" 2012 Go is about language design in the service of software engineering Russ Cox “What is Software Engineering?ˮ

Slide 3

Slide 3 text

© 2024 Thoughtworks | Confidential Go – history 3 Created by Google engineers Robert Griesemer, Rob Pike, and Ken Thompson in 2007 Due to frustration with C complexity and compilation time Publicly announced in November 2009 Version 1.0 released in March 2012 – version 1.24.0 released 11 Feb 2025 😃 Open-sourced with growing community adoption Each C programmer using a different subset of the language!

Slide 4

Slide 4 text

© 2024 Thoughtworks | Confidential Go at Docebo ★ Operational Framework ○ https://gitlab.com/docebo/architecture/operations-framework/opsctl ○ https://gitlab.com/docebo/architecture/operations-framework/tasks-operator ○ https://gitlab.com/docebo/architecture/operations-framework/job-controller ○ https://gitlab.com/docebo/architecture/operations-framework/authorization-service ★ Background Jobs v2 ★ Community (formerly Peerboard) ★ Extraction Team 1 rewriting Catalog Administration in Go ○ https://gitlab.com/docebo/learn/catalog-management/catalog-management-backend ★ Extraction Team 2 rewriting SSO in Go ○ https://gitlab.com/docebo/learn/sso-management/sso-management-backend ★ Extraction Team 4 will rewrite Editing Training Materials in Go 4

Slide 5

Slide 5 text

© 2024 Thoughtworks | Confidential 5 About me (and my employer) ● 20 years with Extreme programming ● 10 years with Thoughtworks ● 2 years with Go Martin Fowler Me We're a leading global technology consultancy that integrates ● strategy, ● design and ● software engineering to enable our clients to thrive.

Slide 6

Slide 6 text

© 2024 Thoughtworks | Confidential 18 gennaio 2006: Prima riunione XPUG-MI! 6 http://milano-xpug.pbworks.com/w/page/20915767/ReportMeet1

Slide 7

Slide 7 text

© 2024 Thoughtworks | Confidential A system of values From my observations of Go and its proponents, it looks like they value: ● Clarity over cleverness ● Libraries over frameworks ● Long-term maintainability over creatorʼs convenience ● Organization productivity over individual preferences 7

Slide 8

Slide 8 text

© 2025 Thoughtworks | Restricted Simple, powerful, fun 8

Slide 9

Slide 9 text

© 2024 Thoughtworks | Confidential Simple and familiar 9 package main import "fmt" func main() { fmt.Println("Hello, world!") } $ go run hello.go Hello, world! $ go build hello.go $ ls hello hello.go $ ./hello Hello, world! $ package main import "fmt" func main() { for n := 1; n <= 100; n++ { switch { case n%15 == 0: fmt.Print("FizzBuzz, ") case n%3 == 0: fmt.Print("Fizz, ") case n%5 == 0: fmt.Print("Buzz, ") default: fmt.Print(n, ", ") } } fmt.Println() } $ go run fizzbuzz.go 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz,

Slide 10

Slide 10 text

© 2024 Thoughtworks | Confidential Easy to learn 10

Slide 11

Slide 11 text

© 2024 Thoughtworks | Confidential Statically typed 11 package main import "fmt" type UserId int type UserName string type UserRole string const Admin = UserRole("admin") type User struct { id UserId name UserName role UserRole } func FindUser(id UserId) User { return User{ id: id, name: "Jane", role: Admin, } } func main() { var user = FindUser(123) fmt.Printf("Found user: %v\n", user) } $ go run types.go Found user: {123 Jane admin} $

Slide 12

Slide 12 text

© 2024 Thoughtworks | Confidential 12 type UserName string type UserStatus struct { name UserName isActive bool logins int } func main() { // Types inferred user := UserStatus{ "Alice", true, 42, } nums := []int{1, 2, 3} sum := 0 for _, n := range nums { sum += n } fmt.Printf("User %s has logged in %d times\n", user.name, user.logins) fmt.Printf("Sum: %d\n", sum) } $ go run types2.go User Alice has logged in 42 times Sum: 6 $ Yet it feels like a scripting language

Slide 13

Slide 13 text

© 2024 Thoughtworks | Confidential What about OOP? ✅ Encapsulation ✅ Methods ✅ Interfaces ✅ Polymorphism ❌ Inheritance 13

Slide 14

Slide 14 text

© 2024 Thoughtworks | Confidential 14 type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func main() { v := Rectangle{3, 4} fmt.Println(v.Area()) } You can define methods… type MyInteger int func (n MyInteger) Square() MyInteger { return n * n } func main() { n := MyInteger(3) fmt.Println(n.Square()) } … on any type you define

Slide 15

Slide 15 text

© 2024 Thoughtworks | Confidential 15 type Shape interface { Area() float64 } type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } And you can define interfaces Rectangle implements Shape just because it has the Area method

Slide 16

Slide 16 text

© 2024 Thoughtworks | Confidential And you have subtype polymorphism 16 type Shape interface { Area() float64 } type Rectangle struct { width, height float64 } func (r Rectangle) Area() float64 { return r.height * r.width } type Triangle struct { base, height float64 } func (t Triangle) Area() float64 { return (t.base * t.height) / 2.0 } type Circle struct { radius float64 } func (c Circle) Area() float64 { return c.radius * c.radius * math.Pi } func main() { shapes := []Shape{ Rectangle{width: 2, height: 3}, Triangle{base: 2, height: 3}, Circle{radius: 1}, } for _, shape := range shapes { area := shape.Area() fmt.Printf("The area of a %14T with %+v is %.2f\n", shape, shape, area) } } $ go build shapes.go $ ./shapes The area of a main.Rectangle with {width:2 height:3} is 6.00 The area of a main.Triangle with {base:2 height:3} is 3.00 The area of a main.Circle with {radius:1} is 3.14 $

Slide 17

Slide 17 text

© 2025 Thoughtworks | Restricted 17 No inheritance?! Rob Pike "Go at Google" 2012 Go encourages composition over inheritance, using simple interfaces that serve as clean, comprehensible boundaries between components. Interface composition is a different style of programming, and people accustomed to type hierarchies need to adjust their thinking, but the result is an adaptability of design that is harder to achieve through type hierarchies.

Slide 18

Slide 18 text

© 2024 Thoughtworks | Confidential Go has closures – like Lisp and JavaScript 18 package main import "fmt" type Multiplier func(int) int func NewMultiplierBy(n int) Multiplier { return func(m int) int { return n * m } } func main() { by3 := NewMultiplierBy(3) by5 := NewMultiplierBy(5) fmt.Println(by3(7)) fmt.Println(by5(9)) } $ go build closures.go $ ./closures 21 45 $

Slide 19

Slide 19 text

© 2024 Thoughtworks | Confidential Lightweight concurrency and message passing, like Erlang/Elixir 19 type Product struct { Id int Name string Price int } func AllProducts(out chan Product) { products := []Product{ {Id: 1, Name: "Laptop", Price: 999}, {Id: 2, Name: "Headphones", Price: 99}, {Id: 3, Name: "Mouse", Price: 49}, {Id: 4, Name: "Monitor", Price: 299}, {Id: 5, Name: "Keyboard", Price: 149}, } for _, product := range products { out <- product } close(out) } func FilterAvailableProducts(in chan Product, out chan Product) { availability := map[int]int{1: 100, 2: 12, 3: 0, 4: 23, 5: 0} for product := range in { if availability[product.Id] > 10 { out <- product } } close(out) } func main() { allProducts := make(chan Product) availableProducts := make(chan Product) go AllProducts(allProducts) go FilterAvailableProducts(allProducts, availableProducts) for product := range availableProducts { fmt.Println(product.Name, "Euro", product.Price) } } $ go build products.go $ ./products Laptop Euro 999 Headphones Euro 99 Monitor Euro 299 $

Slide 20

Slide 20 text

© 2024 Thoughtworks | Confidential Go has parametric polymorphism like TypeScript (kind of) 20 package main import "fmt" type Summable interface { int64 | float64 | string } // T is constrained to implement Summable func Sum[T Summable](numbers []T) T { var sum T for _, n := range numbers { sum += n } return sum } func main() { ints := []int64{1, 2, 3, 4, 5} fmt.Println(Sum(ints)) // 15 doubles := []float64{1.1, 2.2, 3.3} fmt.Println(Sum(doubles)) // 6.6 strings := []string{"a", "b", "c"} fmt.Println(Sum(strings)) // "abc" } $ go build generics.go $ ./generics 15 6.6 abc $

Slide 21

Slide 21 text

© 2025 Thoughtworks | Restricted Software engineering 21 “Software engineeringˮ is what happens to programming when you add time and other programmers Russ Cox

Slide 22

Slide 22 text

© 2024 Thoughtworks | Confidential No unused variables, no unused packages 22 Unused variable or package declarations are a syntax error (not a warning!

Slide 23

Slide 23 text

© 2024 Thoughtworks | Confidential No circular dependencies 23

Slide 24

Slide 24 text

© 2024 Thoughtworks | Confidential One formatting style, automatically enforced ● No debates on how to format code “properlyˮ ● Everyone uses the same style, so they find other peopleʼs code easier to read ● Git merges do not introduce noise due to formatting changes 24

Slide 25

Slide 25 text

© 2024 Thoughtworks | Confidential Go encourages extensive testing 25 Most tests in Go are tabular tests. → Make it easy to add new test cases → Ensure you cover all the cases

Slide 26

Slide 26 text

© 2024 Thoughtworks | Confidential Other built-in tooling go fmt -r 'a[b:len(a)] → a[b:]' ← automatic code rewrite go mod tidy ← download dependencies go vet ← check for risky programming practices govulncheck ← check for vulnerable dependencies 26

Slide 27

Slide 27 text

© 2025 Thoughtworks | Restricted Operational strenghts 27

Slide 28

Slide 28 text

© 2024 Thoughtworks | Confidential Go has a production-ready http server 28 package main import ( "fmt" "log" "net/http" ) func redirectToHello(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, "/hello", http.StatusPermanentRedirect) } func hello(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n") } func main() { mux := http.NewServeMux() mux.HandleFunc("GET /", redirectToHello) mux.HandleFunc("GET /hello", hello) log.Printf("Starting server...") err := http.ListenAndServe(":8080", mux) if err != nil { log.Fatal(err) } }

Slide 29

Slide 29 text

© 2024 Thoughtworks | Confidential Go compiles to a single executable 29 Easily cross-compile to Linux Create the smallest docker container ever! FROM scratch COPY server server CMD ["./server"] $ go build server.go $ ls -lh server -rwxr-xr-x 1 matteo staff 7.1M Feb 4 16:59 server $ file server server: Mach-O 64-bit executable x86_64 $ GOOS=linux GOARCH=amd64 go build server.go $ file server server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=JnXj5owmBFWSgsQbLK1A/NBqFOOGmJYm1CgaE0omK/W14KVm3_5Ec-R3M6w6IP/QGctYFqQXwRjHl39X0 RU, with debug_info, not stripped Dockerfile: $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE server 1.0.0 c44542e853e2 About a minute ago 7.11MB catalog-mgmt latest 288a795280af About a minute ago 21.3MB

Slide 30

Slide 30 text

© 2024 Thoughtworks | Confidential Scalability Goroutines are user-level threads, much cheaper than OS threads ● Goroutines: 2KB of stack at startup, while PHP's processes/threads require 18MB ● A web service handling 10,000 concurrent connections might require: ○ Go: 20MB memory for the stacks 2KB  10,000 goroutines) ○ PHP 10GB memory for the stacks 1MB  10,000 processes) 30 This is a very incomplete treatment, but it hints that you might run Go servers on much cheaper hardware than PHP

Slide 31

Slide 31 text

© 2024 Thoughtworks | Confidential 31 Cancellations

Slide 32

Slide 32 text

© 2024 Thoughtworks | Confidential Cancellation: 32 Abandon work when the user closes or reloads the page This saves resources! (see video) func SleepsALot(w http.ResponseWriter, r *http.Request) { for range 100 { time.Sleep(1 * time.Second) fmt.Println("Sleeping...") err := r.Context().Err() if err != nil { fmt.Printf("Err: %v\n", err) http.Error(w, err.Error(), 499) return } } w.Write([]byte("done!")) } const bindAddress string = "127.0.0.1:8080" func main() { http.HandleFunc("/long-running", SleepsALot) log.Println("Started on", bindAddress) server := &http.Server{Addr: bindAddress} err := server.ListenAndServe() if err != nil { log.Fatal(err) } } This request will take 100s to terminate. It checks every second if its context has been cancelled

Slide 33

Slide 33 text

© 2024 Thoughtworks | Confidential 33 Recursive cancellations

Slide 34

Slide 34 text

© 2024 Thoughtworks | Confidential Recursive cancellation 34 When a request is cancelled, any related I/O operations should also be cancelled (see video) func (h MyHandler) BigSleep(w http.ResponseWriter, r *http.Request) { _, err := h.db.QueryContext(r.Context(), "select sleep(30)") if err != nil { log.Println("error from db:", err.Error()) http.Error(w, err.Error(), http.StatusRequestTimeout) } w.Write([]byte("done!")) } This request will take 30s in the database. The DB driver takes care to cancel the operation if the context is cancelled Http server DB Other server Other server

Slide 35

Slide 35 text

© 2024 Thoughtworks | Confidential 35 Recursive timeouts

Slide 36

Slide 36 text

© 2024 Thoughtworks | Confidential Timeouts too 36 When a request takes too long to process, it should be cancelled and its resources should be reclaimed (see video) func main() { // get a DB connection cfg := mysql.Config{User: "root", Passwd: "", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "mysql"} db, err := sql.Open("mysql", cfg.FormatDSN()) if err != nil { log.Fatal(err) } // Wrap the handler with a timeout decorator handler := http.TimeoutHandler(MyHandler{db}, 5*time.Second, "") http.Handle("/long-running", handler) log.Println("Started on", bindAddress) server := &http.Server{Addr: bindAddress} err = server.ListenAndServe() if err != nil { log.Fatal(err) } } Http server DB Other server Other server

Slide 37

Slide 37 text

© 2025 Thoughtworks | Restricted I’m sold! How do I start? 37

Slide 38

Slide 38 text

© 2024 Thoughtworks | Confidential Resources ● A Tour Of Go: an introduction to all the features of the language ● Go By Example: examples of all the main features ● Learn Go With Tests: a TDD-oriented tutorial ● Go FAQ: answers most common questions ● Effective Go: beyond the syntax ● Go testing by example presentation by Russ Cox ● Go Concurrency Patterns: Context from the go dev blog ● Go with the Domain: DDD and microservices with Go 38

Slide 39

Slide 39 text

© 2024 Thoughtworks | Confidential Go with the source! Matteo Vaccari Technical Principal matteo.vaccari@thoughtworks.com 39