Pico’s Programming: Building Microservices — Part 1

Pico Bolero
6 min readSep 19, 2024

--

Book Review and Notes

Building Microservices by Sam Newman. This blog post isn’t really for reading, but it is the points that I found interesting in the book that I wanted to keep in mind and share with others.

Chapter 1 — Overview

  • How big should a microservice be? Interfaces should be as small as possible. The core concept is that if you don’t release it, you don’t have to maintain it. The interface allows you flexibility to change the implementation behind it. p.9
  • Microservices is an ‘an architecture’ not ‘the architecture’. Before microservices consider a modular monolith: Interfaces and separate data stores p.16
  • A log aggregation tool is so essential that it should be considered a prerequisite before adopting microservices. p.19
  • Use correlation IDs in your logs to enable cross service tracing. p.19
  • Don’t rush to microservices. Let the service boundaries shape up over time. There is a large upfront and continuing maintenance cost that comes with the increased flexibility and scaling. Microservices are not recommended for start-ups as the domain is not established and with a small team of 5 then one person is going to be fully supporting the architecture. Microservices are recommended for established, large projects with many developers

Resources:

  • Log analysis: Humio.com
  • Monitoring jaegertracing.io
  • Cloud Observability Lightstep.com
  • honeycomb.io tracing
  • Kafka streaming data
  • Apache Flink stateful computations over data streams
  • debezium.io Stream changes from your database.

Chapter 2 — Organization

This chapter focused on domain driven development, boundaries, and finding the right balance so that services are loosely coupled and have strong cohesion. The coupling is roughly determined by how many areas you have to edit when a change needs to be made. For instance, if one library changes how many services need to be rebuilt? If a service API changes how many other services need to be updated?

Types of Coupling

  • Domain coupling (best case) when one service relies on another service.
  • Temporal coupling occurs when different concepts are bound together because they happen at the same time.
  • Pass-through coupling occurs when service A provides data to service B only so that service B can pass it to Service C.
  • Common coupling occurs when two services are using the same datastore. If there is ‘state’ in the data it can be hard to manage it. If service A modifies the datastore and service B modifies the same datastore how can you determine if the data is in a correct state?
  • Content coupling (worst case) where Service A modifies Service B’s datastore directly without interacting with Service B.

Ubiquitous language:

Use the same terms in your code and services that your users and domain experts use so everyone is using a common vernacular. If everyone in your organization refers to an soft drink as ‘Pop’ do not code it as ‘Soda’

Chapter 3 — Extracting Microservices

Principle: You don’t “win” by having microservices. Migrate to microservices only if your current architecture cannot meet your goals.

Principle: Do not move to microservices with a big rewrite. Incrementally extract services following the strangler fig pattern.

Principle: Understand what you are trying to achieve before embarking on extracting microservices.

Warning: Separating into microservices is coupled by separating databases by microservice. Fast SQL JOINs between database tables are taken for granted and joining data from multiple separate microservices needs to be accounted for.

Warning: Separating into microservices changes the approach to ‘transactions’ when multiple microservices are involved.

How do you determine which service to extract first?

  • Try the easiest one first, if you cannot get that one to work maybe microservices are not for the team.
  • Hotspots in the code that change frequently and requires frequent redeployment
  • Endpoints that monopolize the memory and CPU and limit total system responsiveness.

Separate the data first? Or separate the code first? Both have tradeoffs.

Extraction Verification:

Parallel Run — run the new microservice along with the current service and verify that both provide the same results by sending the same data to both and getting the same response.

Resources:

Code analysis tool: https://codescene.com/ — view information about your codebase like the ‘hotspots’ where the most frequent changes, identify knowledge islands where a single person has written all the code, identify knowledge transfer areas where code was written by former developers but not the current team.

Chapter 4 — Microservice Communication Styles

Warning: In-Process and Inter-Process communication are vastly different. Inter-Process (service to service) communication is measured in milliseconds and in-process (one application running on a machine) is measured in nanoseconds. Inter-Process communication has to be minimized to reduce the performance hit from serializing, deserializing, and sending packets over a network.

Warning: Error Handling is more complex with services as there are more points of failure: connectivity, network, API changes, other services stalling, being down or OOM.

Blocking Pattern: Synchronous — Call a service and wait for response. Beware of service chaining: Service A calls Service B which calls Service C which calls Service D. Then the wait time is the sum of all parts.

Blocking Pattern: Asynchronous — send multiple requests in parallel (async/await) and continue once they return. Increased complexity to handle failures in the system. Network timeouts, failures, etc.

Non-Blocking Pattern: Common Data — This is like a drop box for files. Service A writes a file to the dropbox and Service B polls the dropbox and acts when there is data. Not great for low-latency but pretty easy to understand.

Non-Blocking Pattern: Request / Response — Implemented using queues where service A adds a Request to QueueB; ServiceB acts on the Request and adds a Response to QueueA; Service A (perhaps not the original submitter of the Request) acts on the Response.

Non-Blocking Pattern: Event-Driven Communication — Service A emits an event to a TopicA. Multiple services subscribe to the TopicA to respond to events. Beware of lack of event details. Was there enough data in the event? Or will every subscriber have to call back to Service A to get more information?

Clarification: Events vs Messages. An event is a fact that something happened. A message is the wrapper around an event’s data. A message is the medium, the event is the payload.

Chapter 5 — Implementing Microservice Communication

This chapter was a bit of a slog. It went over many communications patterns and protocols and their advantages and challenges. It turns out there are a lot of permutations: RPC, REST, GraphQL, Message Brokers, Text Formats, Binary Formats, Schemas, API changes, Service Discovery, Service Meshes, and Service Documentation.

Definition: Tolerant Reader & Postel’s law: “Be conservative in what you do, be liberal in what you accept from others”. Practically speaking, this means that you should make every attempt to make non breaking changes to the API. On the flip side, the client should make every attempt to handle unstable APIs gracefully.

Note: gRPC can add fields as clients don’t care as each field also gets a ‘number’. I was unaware that this was possible with a binary format message.

Format Considerations: The author takes the position that schemas are a way better choice following the philosophy of “Explicit is better than Implicit”. Schemas lead to not as much required documentation, client code stubs that can be generated, validation of message formats. Extending from that, the author asserts that XML was too quickly abandoned for JSON. XML is explicit, it has schemas, rich tooling, and XPath to build Tolerant Readers. After compression, the size difference between JSON and XML is trivial. If there are no schemas, testing must pick up the slack to detect breaking changes.

API Change Considerations: You need to know who your customers are so you can reach them to help them upgrade.

Service Mesh Considerations: Useful for internal SSL connections and load balancing, but the tradeoff is more complexity and it may not be worth it if you have a few services.

Resources: cloudevents.io schema for events (I haven’t looked into this yet.)

Closing

This post only covers Chapters 1–5. See Part 2 for Chapters 6–10.

--

--

Pico Bolero
Pico Bolero

Written by Pico Bolero

A person that wants to make the world a better place. Find me in the fediverse @pico_bolero@sunny.garden

No responses yet