Stefan Zörner
27.01.2023
In unserer schnelllebigen Softwarewelt mit immer neuen Werkzeugen, Technologien und Frameworks zählen Programmiersprachen zu den beständigen Dingen. Die verbreitetsten Vertreter gibt es in der Regel seit Jahrzehnten, C feierte bereits seinen 50. Geburtstag. Go, von manchen als „Kind der Cloud“ bezeichnet, ist im Vergleich dazu tatsächlich fast ein Küken. Was waren die Beweggründe von Google für diese neue Sprache? Und was hat es mit ihr auf sich?
Steckbrief: Programmiersprache Go
Name: Go
Software-Gattung: Programmiersprache
Veröffentlicht: 2009 (Version 1.0: 2012)
Herkunft/Ursprung: Google, Open Source
Zielplattform: Linux (AMD64, PowerPC, …), macOS (x86-64, ARM …), Windows u.v.a.
Programmiersprache(n): Go, früher C (Bootstrapping)
Homepage: go.dev
Viele Jahre lang war es üblich, systemnahe Software in C oder C++ zu programmieren. Betriebssystemkernel wie der von Linux, Datenbanken wie MySQL oder Webserver wie Apache HTTP sind nur einige prominente Beispiele. Für C und C++ sprechen die hohe Effizienz der übersetzten Programme und die vergleichsweise gute Portabilität auf unterschiedliche Hard- und Softwareplattformen.
Im viel zitierten und gleichzeitig oft kritisierten TIOBE-Ranking der beliebtesten Programmiersprachen [TIO] nimmt C noch immer einen Spitzenplatz ein (im Oktober 2022 Platz 2 hinter Python und vor Java). Abbildung 1 zeigt einen Zeitstrahl mit ausgewählten Programmiersprachen ab 1970. Mit Ausnahme von Assembler und Fortran (da vor 1970) ist die aktuelle Top-20 des TIOBE-Index komplett enthalten. Die aktuellen Top-10-Sprachen sind allesamt mehr als 20 Jahre alt.
Viele moderne systemnahe Softwarelösungen insbesondere im Cloud-Umfeld sind heute allerdings nicht in C oder C++ geschrieben, sondern in Go. Die Cloud Native Landscape [CNCF] belegt dies eindrucksvoll. Das zugehörige Projekt kategorisiert und kartographiert mehr als 1000 Projekte und Produkte und sorgt für Ordnung im Cloud-Dschungel. Betrachten wir die 100 gemessen an GitHub-Sternen beliebtesten dort verzeichneten Open Source-Lösungen!
Stand Oktober 2022 sind weit mehr als 40% davon primär in Go entwickelt. Darunter finden sich bekannte Vertreter wie Kubernetes zur Container-Orchestrierung oder Prometheus für Monitoring und Alerting. Abbildung 2 macht das Ganze in einer Tortengrafik und einer Word Cloud generiert aus allen Top-100 Projekten sichtbar. Die Font-Größe in der Word Cloud hängt dabei vom Zuspruch in Form von GitHub-Sternen ab, die Farbe signalisiert die Programmiersprache. Go färbt die Wortwolke blau. Ein Kind der Cloud?
Go ist ab 2007 bei Google entstanden und wurde 2009 als Open-Source veröffentlicht. Von den drei Köpfen hinter der Sprache – Robert Griesemer, Rob Pike und Ken Thompson – ist Ken Thompson der Bekannteste. Der Turing-Preisträger war bereits maßgeblich an der Entwicklung von Unix beteiligt.
Hauptmotivation bei Google für Go war die Unzufriedenheit mit dem dort viel verwendeten C/C++. Vor allem die langen Übersetzungszeiten, welche die Entwicklung großer Softwaresysteme verlangsamten, störten. Go sollte C und C++ bei Google ersetzen und auch mit großen Softwaresystemen, an denen viele Teams gemeinsam arbeiten, effizient umgehen können. Tabelle 1 enthält dieses und weitere wichtige Ziele für Go.
Ziel | Beschreibung |
Skalierbar bezüglich der Programmgröße | Mit Go lassen sich Programme jeder Größe mit angemessenem Aufwand realisieren. Von kleinen Kommandozeilenwerkzeugen bis hin zu sehr großen Systemen. |
Hohe Effizienz in Lauf- und Übersetzungszeiten | Go unterstützt die Entwicklung hochperformanter Anwendungen und Dienste für moderne Serverplattformen. Die Programme sind flink übersetzt, ihre Effizienz ist voraussehbar gut. Auch systemnahe Software wie Netzwerkserver oder Datenbanken sind gut realisierbar. |
Leicht portierbare Programme | in Go verfasste Programme sind leicht auf andere Hardwareplattformen und Betriebssysteme übertragbar. Das gilt auch für das Go-Tooling, also etwa den Compiler. |
Moderne Concurrency-Konzepte | Mit der Sprache ist es vergleichsweise einfach nebenläufige Programme zu schreiben. Die Ausführung auch kleinteilig paralleler Abläufe erfolgt in Go auf effiziente Art und Weise. |
Fühlt sich an wie eine dynamische Sprache | In Go zu programmieren geht trotz statischer Typisierung sehr leicht von der Hand. Die Sprache wählt wo möglich selbst "das Richtige" aus. |
Konzeptionelle Einfachheit der Sprache | Go besticht durch eine überschaubare und zugleich schlüssige Auswahl an Sprach-Features. Es verzichtet auf "heikle" Programmierkonstrukte und verbirgt nötige Komplexität geschickt. Go ist als Sprache daher vergleichsweise leicht zu erlernen. |
Go umfasst die Programmiersprache selbst mit ihrer Syntax, die zugehörigen Tools wie beispielsweise den Compiler und auch eine Laufzeitumgebung (Runtime). Abbildung 3 verdichtet die Situation. Verschiedene Entwurfsentscheidungen und Lösungsansätze in diesen Bereichen begünstigen die Erreichung der gesteckten Ziele.
So kommt Go mit großen Codebasen zum einen durch seinen sehr effizienten Compiler zurecht. Zum anderen halten Modularisierungskonzepte wie Pakete und ab Go Version 1.11 auch Module für explizites Abhängigkeitsmanagement mit Versionen umfangreiche Lösungen beherrschbar (siehe [Bui+2015]).
Die Go-Standard-Bibliothek stellt Funktionalität für alle heute übliche Aufgaben bereit, speziell fürs Web. Go hatte den Vorteil als Neuling hier lernen zu können. Wo andere Sprachen auf Fremdbibliotheken oder separate Tools angewiesen sind oder zumindest lange Zeit waren, ist hier „alles schon dabei“. Themen wie beispielsweise die Verarbeitung von JSON, Logging, Unit-Tests, Benchmarks oder das Formatieren von Quelltext sind durch Go selbst bzw. seine Standardbibliothek abgedeckt. Ein produktionsreifer HTTP(/2)-Server ist ebenso enthalten wie umfassende Crypto-Funktionalität und Templating. Nichtsdestotrotz sind auch für Go zahlreiche 3rd Party-Bibliotheken und Frameworks verfügbar (eine umfassende Übersicht liefert [AWG]).
Für hohe Performance zur Laufzeit übersetzt Go den Quelltext wie C in ein natives Programm zur Ausführung direkt auf dem Zielsystem. Das Ergebnis ist ein einziges Executable, das auch die Go-Runtime enthält. Teil davon ist eine sehr effizienter Garbage Collector.
Go-Programme lassen sich leicht auf andere Plattformen portieren. Der Cross-Compiler unterstützt Binärformate, die sich von der Host-Plattform unterscheiden. Er läuft dabei beispielsweise auf Linux (z.B. auf AMD64) und erzeugt Programme für macOS (z.B. ARM), Windows etc. Neben der Go Runtime landen auch alle Dependencies statisch gelinkt im Kompilat. Mittlerweile ist auch der Go-Compiler in Go geschrieben und wird mit sich selbst übersetzt (Details siehe [Cox2015]). Auch die Standard Libraries von Go sind nahezu komplett in Go geschrieben. Go sichert sowohl für die Sprachspezifikation als auch für die Standardbibliothek innerhalb der 1.x-Versionen Abwärtskompatibilität auf Quelltextebene zu.
Anders als bei C unterstützt Go Nebenläufigkeit direkt in der Sprache. Go-Routinen sind leichtgewichtige Threads im Userland, Kanäle erlauben es diesen sicher zu kommunizieren (vgl. CSP-Modell von Hoare [Hoare1985]). Es gibt spezielle Schlüsselwort dazu („go“, „chan“) und eigene Operatoren für Channels. Die Go-Runtime kann mit seinem Scheduler Unmengen dieser Go-Routinen steuern und bildet sie auf Betriebssystem-Threads ab. Die Garbage Collection erleichtert das Schreiben nebenläufiger Programme.
Weitere Sprachmittel von Go sorgen für eine Leichtigkeit, wie wir sie von Skriptsprachen kennen. Die Typinferenz führt etwa zu kompakten Schreibweisen in Deklarationen mit Zuweisungen (konkretes Beispiel: „zahl := 6“ ist gleichbedeutend mit „var zahl int = 6“). Typen können zuweisungskompatibel zu einem Interface sein, ohne es explizit zu implementieren.
Das wesentliche Entwurfsziel der Sprache selbst war die Einfachheit (siehe [Pike2015]). Go hat nur 25 Schlüsselwörter und insgesamt einen kleinen Sprachumfang. „Heikle“ Sprachfeatures wie die Zeigerarithmetik in C lässt es aus. Es gibt nur wenige Möglichkeiten, Dinge zu tun (Beispiel: nur ein Schleifenkonstrukt). Beim Error Handling verzichtet Go auf Exceptions und kommuniziert Fehler über zusätzliche, explizite Rückgabewerte. Ein versteckter Kontrollfluss über geworfene Ausnahmen entfällt. Zudem forcieren die Tools guten Programmierstil und eine einheitliche Formatierung. Entwickler/innen finden sich in fremdem Go-Quelltext dadurch schnell zurecht.
Wenn Sie selbst neugierig geworden sind, wie sich die Sprache „anfühlt“: Der Go Playground bietet eine schöne Möglichkeit zum Probieren. Dies geschieht Browser-basiert, der Code wird serverseitig übersetzt und ausgeführt. „A Tour of Go“ [Tour] bietet eine interaktive Einführung, der Go Playground ist dabei in die Seiten integriert (siehe Abbildung 5).
In Summe haben die beschriebenen Eigenschaften dazu geführt, dass die Sprache im systemnahen Umfeld (Middleware, Protokoll-Server, …) in den letzten Jahren viel Verbreitung fand. In den Open Source-Projekten der Cloud Native Landscape nimmt Go weiterhin zu, neben den bereits genannten prominenten Vertretern wären Traefik (Reverse Proxy und Load Balancer), Istio (Service Mesh) oder jüngst Caddy (Web-Server) weitere. Oder Hugo, ein beliebter Generator für statische Webseiten.
Aber eignet sich Go auch für Anwendungssoftware? Tatsächlich spricht wenig dagegen, solange es sich nicht um Desktop-Software handelt. Insbesondere für schlanke Dienste, die in Containern laufen sollen, ist Go mittlerweile recht verbreitet. Die zugehörigen Images geraten Dank des nativen, nur aus einem Executable ohne weitere Abhängigkeiten bestehenden Programmes sehr klein. Die Programme starten schnell und die mitgelieferte Unterstützung z.B. für einen REST-Endpunkt via HTTP erfüllt die üblichen Anforderungen bzgl. Skalierbarkeit und Effizienz spielend.
Ob Go langfristig C und C++ den Rang ablaufen, und an Bedeutung gewinnen oder sie zumindest erhalten kann, wird sich zeigen. Tatsächlich ist es nicht der einzige Herausforderer. Mit Rust steht eine weitere Programmiersprache in den Startlöchern, die einige Gemeinsamkeiten (z.B. das Übersetzen in nativen Code), aber auch einige Unterschiede zu Go (z.B. keine Garbage Collection) aufweist. John Arundel hat in seinem Blog einen lesenswerten und undogmatischen Vergleich zwischen Rust und Go geschrieben Aru2021].
[Aru2021] John Arundel: “Rust vs. Go”, Blog-Beitrag, Mai 2021
[AWG] Awesome Go, “A curated list of awesome Go frameworks, libraries and software”
[Bui+2015] Tyler Bui-Palsulich et al.: “Using Go Modules”, Blog-Serie
[Cox2015] Russ Cox: “Go 1.5 Bootstrap Plan”, 2015
[CNCF] Cloud Native Computing Foundation: “Cloud Native Landscape”
[Hoare1985] C.A.R. Hoare: “Communicating Sequential Processes”, Prentice Hall 1985
[Pike2015] Rob Pike: “Simplicity is Complicated”, Vortrag auf der dotGo 2015
[TIO] TIOBE: TIOBE Programming Community Index, Oktober 2022
[TOUR] „A Tour of Go“, interaktives Tutorial
Dieses Porträt ist ursprünglich in der IT Spektrum, Ausgabe 1 | 2023 als vierter Teil einer Reihe über Architektur-Ikonen der Softwareentwicklung erschienen. Rückmeldungen aller Art gerne an mich per E-Mail. Insbesondere auch Wünsche für weitere Porträts.
Architektur-Porträt: Corona-Warn-App
Architektur-Porträt: Die Graphdatenbank Neo4j
Architekturikonen in Software Überblick über alle Porträts