platform supports 30,000 nodes in a single cluster. Moving to Kubernetes has increased their R&D efficiency by more than 100% and deployment efficiency by 280%. [link] JD currently runs the world’s largest Kubernetes cluster in production. [link] • 77,126 commits made by 2,561 contributors • 1,307,371 lines of code • mostly written in Go
Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines, Strong type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language. https://golang.org/doc/
an operating system of your choice when compiling a piece of code. • Go offers a native concurrency model that is unlike most mainstream programming languages. Go relies on a concurrency model called CSP ( Communicating Sequential Processes). Instead of locking variables to share memory, • Go has a fairly mature package of its own. • Go code typically compiles to a single native binary. • Go is also being rapidly being adopted as the go-to cloud native language.
modern OOP languages. • No classes. Everything is divided into packages only. Go has only structs instead of classes. • Does not support inheritance. That will make code easy to modify. In other languages like Java/Python, if the class ABC inherits class XYZ and you make some changes in class XYZ, then that may produce some side effects in other classes that inherit XYZ. • By removing inheritance, Go makes it easy to understand the code also (as there is no super class to look at while looking at a piece of code). We will come back to OOP later...
its execution environment. This could include: • The operating system on which the app runs • The VM or interpreter which runs the code • Packages which are imported in the code Go handle dependency at build time, not in the hands of the user. GOOS=windows GOARCH=386 go build -o hello.exe hello.go GOOS=darwin GOARCH=amd64 go build hello.go
lock manager for Google's GFS distributed filesystem. He has also worked on the design and implementation of the domain-specific language Sawzall, the Java HotSpot virtual machine, and the Strongtalk system. He has also written a vectorizing compiler for the Cray Y-MP and an interpreter for APL. https://alchetron.com/Robert-Griesemer
to help solve Google's problems, and Google has big problems. 1. The hardware is big and 2. the software is big. 3. Thousands of engineers work on the code And of course, all this software runs on zillions of machines, which are treated as a modest number of independent, networked compute clusters.
into packages and each compiled package file imports transitive dependency info. If A.go depends on B.go depends on C.go: • compile C.go, B.go, then A.go. • to recompile A.go, compiler reads B.o but not C.o. At scale, this can be a huge speedup
OpenOffice, Chromium) have huge build times. On a Mac (OS X 10.5.7, gcc 4.0.1): • C: #include <stdio.h> ◦ reads 360 lines from 9 files • C++: #include <iostream> ◦ reads 25,326 lines from 131 files • Objective-C: #include <Cocoa/Cocoa.h> ◦ reads 112,047 lines from 689 files But we haven't done any real work yet! • In Go, import "fmt" reads one file: ◦ 195 lines summarizing 6 dependent packages As we scale, the improvement becomes exponential.
connected system. Go concurrency makes manage connections and clients easier, and so performance. Go provides independently executing goroutines that communicate and synchronize using channels.
are small, segmented, sized on demand • goroutines are multiplexed by demand onto true threads • requires support from language, compiler, runtime • can't just be a C++ library
languages we use event-driven callbacks and continuations. But in Go, a goroutine per connection model scales well. for { rw := socket.Accept() conn := newConn(rw, handler) go conn.serve() }
expensiveComputation(x, y, z) anotherExpensiveComputation(a, b, c) } • Need to know when the computations are done. • Need to know the result. A Go channel provides the capability: a typed synchronous communications mechanism.
• Go get • Go test • Go profile • Go mod https://www.alexedwards.net/blog/an-overview-of-go-tooling Especially gofmt makes single programming style, everyone’s code looks the same. Which makes writing relate tool easier.
for symbol search in the current file • go-symbols for symbol search in the current workspace • gopkgs for auto-completion of unimported packages • guru for the Find all References feature • gorename for renaming symbols • goreturns or goimports for formatting code • godef or gogetdoc for the Go to Definition feature • godoc or gogetdoc for the documentation that appears on hover • golint or gometalinter or megacheck or golangci-lint or revive for linting • dlv for debugging
into programming language design; it is to improve the working environment for its designers and their coworkers. Go is more about software engineering than programming language research. Or to rephrase, it is about language design in the service of software engineering. But what is software engineering? [link] Software engineering is what happens to programming when you add time and other programmers.
exciting! If you like stability. Stable foundation, build stuff on top Excitement higher in the stack, and in non-language areas of the foundation Excitement besides the language… https://golang.org/doc/go1compat
than most general-purpose programming languages, Go was designed to address a set of software engineering issues that we had been exposed to in the construction of large server software. That might make Go sound rather dull and industrial, But in fact the focus on clarity, simplicity and composability throughout the design instead resulted in a productive, fun language that many programmers find expressive and powerful. We will come back to expressiveness later...
dependencies • no box (constants are just numbers) • no public,private keywords (letter case sets visibility) • no un-used variable (compiling fail) • no memory management (manage automatically)
subtype inheritance (no subclasses) • interfaces are implicit (no "implements" declaration) • embedding (no promotion to superclass) • methods are declared as functions (no special location) • methods are just functions (same receiver as first argument) • interfaces are just methods (no data, behavior contract only) • methods match by name only (not by type) • no constructors or destructors • no implicit "this" in methods
• (the implementation might still be sophisticated) Orthogonal • concepts mix cleanly • easy to understand and predict what happens Succinct • no need to predeclare every intention Safe • misbehavior should be detected These combine to give expressiveness.
• The (p Point) declares the receiver (no automatic " this " • variable; also notice p is not a pointer, although it could be.) • Methods are not mixed with the data definition. • They are orthogonal to types. type Point struct { x, y float } func (p Point) Abs() float { return math.Sqrt(p.x*p.x + p.y*p.y) }
methods allows any type to have them. • Orthogonality leads to generality. type Vector []float func (v Vector) Abs() float { sumOfSquares := 0.0 for i := range v { sumOfSquares += v[i]*v[i] } return math.Sqrt(sumOfSquares) }
any type. • Interfaces are satisfied implicitly. • Point and Vector do not declare that they implement Abser , they just do. type Abser interface { Abs() float } var a Abser a = Point{3, 4} print(a.Abs()) a = Vector{1, 2, 3, 4} print(a.Abs())
concrete type such as structs define data 2. interfaces define behavior • As with methods, now anything can satisfy an interface. type Value float // basic type func (v Value) Abs() float { if v < 0 { v = -v } return float(v) } a = Value(-27) print(a.Abs())
method sets Orthogonal • representation (data) and behavior (methods) are independent • empty interface can represent any value Succinct • no implements declarations; interfaces just satisfy Safe • static type checking Expressiveness: implicit satisfaction lets pieces fit together seamlessly.
outside package ◦ ThisNameIsPublic ◦ thisOneIsNot • simple, easy rule to understand consequences clear • see a variable, can see whether it's public without going to the declaration • can make any type, variable, or constant public or not with the same mechanism Orthogonality again!
send (arrow points in direction of flow): ch <- value Channel receive: value = <-ch Channels are unbuffered by default, which combines synchronization with communication.
goroutines too cheap to meter Orthogonal • concurrency orthogonal to rest of language • orthogonality of functions make closures easy Succinct • go f() • closure syntax clear Safe • no stack overflows • garbage collection avoids many concurrency problems Expressiveness: complex behaviors easily expressed.
new language versions (Java 8, ECMAScript 6, C#, C++14) and were lists of features. • These languages evolve and compete by adding features. • The languages grow in complexity while becoming more similar. • Bloat without distinction.
fewer features), but enable more ) • Go does not try to be like the other languages. • Go does not compete on features. • As of Go 1, the language is fixed. • Many newcomers to Go ask for features from languages they know. • But those features do not belong in Go—and the language is fixed. • Adding features to Go would not make it better, just bigger.
operation: the only way allocated memory returns to the pool is through the garbage collector. In C and C++, too much programming effort is spent on memory allocation and freeing. In a concurrent object-oriented language it's almost essential to have automatic memory management because the ownership of a piece of memory can be tricky to manage as it is passed around among concurrent executions. It's important to separate behavior from resource management. The language is much easier to use because of garbage collection.
significant costs: general overhead, latency, and complexity of the implementation. Nonetheless, we believe that the benefits, which are mostly felt by the programmer, outweigh the costs.
by controlling the layout of data structures. In Go it is easy and efficient to provide second-order allocators, for instance an arena allocator that allocates a large array of structs and links them together with a free list. Libraries that repeatedly use many small structures like this can, with modest prearrangement, generate no garbage yet be efficient and responsive. A knowledgeable programmer can limit the pressure placed on the collector and thereby improve performance.
full set of unsigned types • bit-level operations • programmer control of memory layout type T struct { x int buf [20]byte ... } // pointers to inner values p := &t.buf
for concurrent programming; otherwise it's too fussy and error-prone to track ownership as data moves around. C++ libraries is about deciding who owns memory, who destroys resources. Memory management must be hidden behind the interface. But garbage collection isn't enough.
but no pointer arithmetic • no dangling pointers (locals move to heap as needed) • no pointer-to-integer conversions* • all variables are zero-initialized • all indexing is bounds-checked Should have far fewer buffer overflow exploits. • Package unsafe allows this but labels the code as dangerous; used mainly in some low-level libraries.
programmer instructs the machine how to change its state, • procedural which groups instructions into procedures, • object-oriented which groups instructions together with the part of the state they operate on declarative in which the programmer merely declares properties of the desired result, but not how to compute it https://en.wikipedia.org/wiki/Programming_paradigm
decide what piece goes in what box, every type's parent, whether A inherits from B or B from A. It's a preposterous way to think about programming. What matters isn't the ancestor relations between things but what they can do for you.
approach to object-oriented programming, allowing methods on any type, not just classes. Without any form of type-based inheritance like subclassing. This means there is no type hierarchy. This was an intentional design choice.
designed early, often as the first step of designing the program, and early decisions can be difficult to change once the program is written. As a consequence, the model encourages early overdesign as the programmer tries to predict every possible use the software might require, adding layers of type and abstraction just in case. This is upside down. The way pieces of a system interact should adapt as it grows, not be fixed at the beginning.
satisfy this interface implicitly; there is no implements declaration Interface satisfaction is statically checked at compile time so despite this decoupling interfaces are type-safe.
a Write method with this signature works well with the complementary Reader interface: type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) }
and writer methods allow type-safe chaining with rich behaviors, like generalized Unix pipes. Files, buffers, networks, encryptors, compressors, image encoders, and so on can all be connected together. The Fprintf formatted I/O routine takes an io.Writer rather than, as in C, a FILE*. The formatted printer has no knowledge of what it is writing to; It may be a image encoder that is in turn writing to a compressor that is in turn writing to an encryptor that is in turn writing to a network connection.
hierarchy also eliminates a form of dependency hierarchy. Interface satisfaction allows the program to grow organically without predetermined contracts. And it is a linear form of growth; a change to an interface affects only the immediate clients of that interface; there is no subtree to update. The lack of implements declarations disturbs some people but it enables programs to grow naturally, gracefully, and safely.
every Read call on the incoming Reader.LimitingReader stops reading after n bytes. ErrorInjector aids testing by simulating I/O errors. And there are many more. • func LoggingReader(r io.Reader) io.Reader • func LimitingReader(r io.Reader, n int64) io.Reader • func ErrorInjector(r io.Reader) io.Reader The designs are nothing like hierarchical, subtype-inherited methods. They are looser (even ad hoc), organic, decoupled, independent, and therefore scalable.
of components. It doesn't matter what that thing is, if it implements method M I can just drop it in here. Another important example is how concurrency gives us the composition of independently executing computations. And there's even an unusual (and very simple) form of type composition: embedding.
all-encompassing. You don't get everything built in. You don't have precise control of every nuance of execution. For instance, you don't have RAII(constructor and destructor). Instead you get a garbage collector. You don't even get a memory-freeing function. • What you're given is a set of powerful but easy to understand, easy to use building blocks from which you can assemble—compose—a solution to your problem. • Compose method are easier to write, easier to read, easier to understand, easier to maintain, and maybe safer. ◦ No need to plan ahead ( as type hierarchy ) ◦ replace any component as you wish
Assembly with Go https://github.com/grpc/grpc-go ( rpc server and client generator ) https://github.com/kubernetes/code-generator Examples include generating Unicode tables in the unicode package, creating efficient methods for encoding and decoding arrays in encoding/gob, producing time zone data in the time package https://github.com/GeertJohan/go.rice ( non-go file build in )
explicitness, It was a deliberate choice not to incorporate exceptions in Go. • First, there is nothing truly exceptional about errors in computer programs. For instance, the inability to open a file is a common issue that does not deserve special linguistic constructs; if and return are fine. f, err := os.Open(fileName) if err != nil { return err } • Also, if errors use special control structures, error handling distorts the control flow for a program that handles errors.
blocks interlaces multiple overlapping flows of control that interact in complex ways. • Although in contrast Go makes it more verbose to check errors, the explicit design keeps the flow of control straightforward—literally. • There is no question the resulting code can be longer, but the clarity and simplicity of such code offsets its verbosity. • Explicit error checking forces the programmer to think about errors—and deal with them—when they arise. Exceptions make it too easy to ignore them rather than handle them, passing the buck up the call stack until it is too late to fix the problem or diagnose it well.
programmed • Since errors are values, errors can be programmed. • Can be programmed in different ways to suit different situations. • The full power of the Go programming language is available for processing them. https://blog.golang.org/errors-are-values handle errors with grace.
like a standard library for the cloud. You can write an application, and just compile it for Mac, compile it for Linux or Windows or different architectures, and it just runs, and it runs without really any extra code, go-cloud’s goal is do the same thing for cloud https://blog.golang.org/go-cloud https://blog.golang.org/gcdk-whats-new-in-march-2019