Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Why Go?

Why Go?

Why Go? The story of how I came to like this language, told not only by its strengths as a language, but also by the values that, it seems to me, are implicitly or explicitly expressed by Go's proponents. All in my opinion!

Presented originally for Docebo, then for the Milano XPUG (Extreme Programming User Group)

Matteo Vaccari

March 21, 2025
Tweet

More Decks by Matteo Vaccari

Other Decks in Technology

Transcript

  1. © 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?ˮ
  2. © 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!
  3. © 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
  4. © 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.
  5. © 2024 Thoughtworks | Confidential 18 gennaio 2006: Prima riunione

    XPUG-MI! 6 http://milano-xpug.pbworks.com/w/page/20915767/ReportMeet1
  6. © 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
  7. © 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,
  8. © 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} $
  9. © 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
  10. © 2024 Thoughtworks | Confidential What about OOP? ✅ Encapsulation

    ✅ Methods ✅ Interfaces ✅ Polymorphism ❌ Inheritance 13
  11. © 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
  12. © 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
  13. © 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 $
  14. © 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.
  15. © 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 $
  16. © 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 $
  17. © 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 $
  18. © 2025 Thoughtworks | Restricted Software engineering 21 “Software engineeringˮ

    is what happens to programming when you add time and other programmers Russ Cox
  19. © 2024 Thoughtworks | Confidential No unused variables, no unused

    packages 22 Unused variable or package declarations are a syntax error (not a warning!
  20. © 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
  21. © 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
  22. © 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
  23. © 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) } }
  24. © 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
  25. © 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
  26. © 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
  27. © 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
  28. © 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
  29. © 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
  30. © 2024 Thoughtworks | Confidential Go with the source! Matteo

    Vaccari Technical Principal matteo.vaccari@thoughtworks.com 39