TL;DR notes from articles I read today.
- Use test suites - develop tests written against an interface for all implementations of that interface.
- Carefully consider interfaces before exporting them and avoid creating a hard dependency between a consumer package and your own. To avoid exporting an interface, use an internal/package subtree to keep the interface scoped to the package.
- Don’t export concurrency primitives, especially channels and the sync package. Also, add documentation on whether a struct or package is safe for concurrent access by multiple goroutines.
- Use net/http/httptest to speed up tests and run them in parallel more easily, without binding to a port or setting up a server.
- Use a separate _test package inside the foo/ directory of the package you want to test, rather than the default package pkg. This is a workaround for cyclic dependencies, prevents brittle tests and lets you see what it is like to consume your own package.
Full post here, 8 mins read
- Add comprehensive info-level logging coverage to make each step of processing more observable. Structure logging with log levels using the logrus package to eliminate noise and improve searchability within logs, and impose consistency.
- Create a structured error type to include a generic map of data. This will help you diagnose what exactly went wrong at the top level when error handling, indicating which errors are fatal and which indicate you should retry processing.
- Use Go’s context package, threading it through all request handlers and message processors for greater control over your services, making for a more reliable shutdown in case of new deployment or scaling events.
- Combine the detailed errors and logging into a common abstraction to reduce code repetition. You can add functions for creating errors and logs with basically the same interface, in turn using the context data.
- Add service-level indicators (SLIs) for high-level functions in asynchronous processing to reveal end-to-end latency of different kinds of processing to end-users.
Full post here, 6 mins read
Some learnings from Grab’s implementation of the principles of domain-driven development (DDD) and idiomatic Go.
- Work closely with business/domain experts to gather knowledge on the required functionality and flow of what you are building.
- Break down this knowledge into different problems that need to be solved. Categorize these problems into bounded contexts and subcontexts (read here to know more about it).
- Use these contexts to identify dependencies in the code & in the team. Identify the building blocks (value objects and entities) to further break down the functionality and flow.
- Create interfaces to abstract the working logic of a given domain (i.e. repository).
- Identify domain events and let them provide the relevant useful information to the user in other domains. This enables the independence of different classes.
- Use a common language developed with the domain experts and apply it consistently in discussions and coding (for instance, to name classes and methods).
Full post here, 5 mins read