Einleitung: Mythen und Realität eines Go-Programmierers
\n\nIn der IT-Community gibt es zwei gegensätzliche Meinungen über die Arbeit eines Go-Entwicklers. Die einen sagen, es sei das Paradies für Perfektionisten: strenge Typisierung, keine Klassenvererbung und blitzschnelle Kompilierung. Die anderen sagen, es seien endlose if err != nil und der Kampf mit Generics (obwohl sie bereits hinzugefügt wurden).
Wo liegt die Wahrheit? Wie sieht ein Arbeitstag eines Go-Entwicklers in einem durchschnittlichen Unternehmen aus, das hochlastige Microservices schreibt? Heute öffne ich die Tür zu meiner Realität. Kein Blabla – nur Code, Tools und echte Fälle.
\n\nIch arbeite in einem Produktunternehmen, in dem Golang die Hauptsprache für das Backend ist. Unser Stack: gRPC, PostgreSQL, Kafka, Kubernetes. Eine typische Geschichte für die moderne Entwicklung mit Go. Los geht's.
\n\n08:30 – Morgen: Plan und Code-Review
\n\nDer Tag beginnt nicht mit Kaffee, sondern mit dem Durchsehen von Benachrichtigungen in GitLab und Slack. Während das Gehirn noch frisch ist, ist dies die beste Zeit, um Merge Requests von Kollegen zu überprüfen.
\n\nMorgendliche Routine: Was schaue ich mir zuerst an?
\n- \n
- Fehler in CI/CD – wenn der Build über Nacht gefallen ist, ist das die erste Aufgabe. \n
- Kommentare zu meinem Code – in Go sind detaillierte Reviews üblich. \n
- Monitoring-Diagramme – ist die Latenz nach dem gestrigen Deployment eingebrochen? \n
Übrigens, zum Code-Review. In der Go-Community gibt es eine ungeschriebene Regel: Code sollte einfach sein. Wenn Sie ein kompliziertes Rad neu erfinden, werden Sie beim Review abgewiesen. Ein Beispiel für einen typischen Kommentar eines Seniors:
\n\n// Schlecht: übermäßige Abstraktion\ntype Processor interface {\n Process(ctx context.Context, data []byte) ([]byte, error)\n}\n\n// Gut: einfach eine Funktion\nfunc Process(ctx context.Context, data []byte) ([]byte, error) {\n // Logik\n}\n\n\nUm 09:00 Uhr – das tägliche Standup. Wir verschwenden keine Zeit mit langen Erzählungen. Format: was ich gestern gemacht habe, was ich heute machen werde, gibt es Blocker. Kein „Wie war Ihr Abend?“.
\n\n09:30 – Eintauchen in den Code: Einen Microservice mit Go schreiben
\n\nHeute steht die Überarbeitung des Benachrichtigungsdienstes auf dem Plan. Dies ist ein klassischer Microservice mit Go, der Ereignisse aus Kafka empfängt und E-Mails/Push-Nachrichten versendet. Aufgabe: Unterstützung für Vorlagen hinzufügen.
\n\nAls erstes öffne ich die Projektstruktur. In Go ist ein minimalistischer Ordnerbaum üblich:
\n\nnotifier/\n├── cmd/\n│ └── server/\n│ └── main.go\n├── internal/\n│ ├── handler/\n│ ├── service/\n│ └── repository/\n├── pkg/\n│ └── models/\n└── go.mod\n\n\nIch beginne mit dem Datenmodell. In Go wird für die Arbeit mit der Datenbank oft Go ORM (z. B. GORM) oder reine SQL-Abfragen über sqlx verwendet. Ich bevorzuge sqlx – weniger Magie, mehr Kontrolle.
\n\ntype Template struct {\n ID int64 `db:"id"`\n Name string `db:"name"`\n Content string `db:"content"`\n CreatedAt time.Time `db:"created_at"`\n}\n\nfunc (r *Repository) GetTemplateByName(ctx context.Context, name string) (*Template, error) {\n var t Template\n err := r.db.GetContext(ctx, &t, "SELECT * FROM templates WHERE name = $1", name)\n if err != nil {\n return nil, fmt.Errorf("get template: %w", err)\n }\n return &t, nil\n}\n\n\nBeachten Sie die Fehlerbehandlung. In Golang gibt es kein try-catch. Jeder Funktionsaufruf wird auf Fehler überprüft. Das ist kein Bug, sondern ein Feature – der Code wird vorhersagbar. In 5 Jahren Arbeit habe ich noch nie einen unerwarteten Null-Pointer in der Produktion erwischt.
\n\n11:00 – Integration mit gRPC
\n\nDer nächste Schritt ist das Hinzufügen eines gRPC-Endpunkts für das Admin-Panel. In der modernen Entwicklung mit Go ist gRPC zum Standard für interne Dienste geworden. Es ist schneller als REST und generiert Clients automatisch.
\n\nZuerst beschreibe ich die Proto-Datei:
\n\nsyntax = "proto3";\n\npackage notifier;\n\nservice TemplateService {\n rpc CreateTemplate (CreateTemplateRequest) returns (TemplateResponse);\