Sensibly Sized Services — part 2

With the easy part out of the way (making fun of microservices in part 1), it’s time for part 2. Here I’ll try and define what a sensible size for a service actually is.

Not a Monolith

I’m tempted to argue that anything that is not a monolith is not too big. As we know, monoliths should be avoided because they are hard to change. Difficulty in changing these stone structures of software can be due to the size of the codebase, but total lines of code do not tell the whole story. I’ve worked on applications with large codebases where it has been easy to find the right place to put the new logic, because the applications had been well designed and there was good oversight by senior developers. Automated tests gave me confidence that my changes were correct and were not going to break production. The codebases were large, reusing existing functionality was simple, and the software was easy to change. Those services were not monoliths, despite their size.

Here are some indicators to determine the monolithicness (sorry) of your service:

  • Lead time. How long does it take to get a change of moderate complexity into production, from inception to release?
  • How quickly can you build and run automated tests over the service?
  • Can the service be deployed to production quickly and easily with few if any manual steps?
  • How stable is the service in production and test environments?
  • How many bugs are found after development of a feature of moderate complexity?
  • How confident are you as a developer that you are making your code change in the right spot, and that it’s not going to have any unwanted side effects?
  • How confident are you as a senior developer that patterns and practices are being followed within your service’s codebase?
  • How much time is being wasted managing source control branching and merging?

The above indicators are not concrete, but they don’t have to be perfectly accurate to give an indication of service monolithicness (sorry again). In fact you could go through these points in a retro with your team with simple ‘red amber green’ answers for the more subjective questions. If lead times are high and confidence in change is low, you have a problem. You have a monolith. Conversely, if confidence is high and lead times are low, why worry if the number of lines of code is approaching 1 million? You shouldn’t be worried, you should be happy. You have a service that’s easy to change that has none of the drawbacks of microservices.

If your service has monolithic tendencies then you have a decision to make. You can focus on improving the problem area (speeding up the build, automating the release process, applying stricter code reviews) or you can look at splitting functionality out into separate services. If you do split out to a separate service, make sure that you are aware of the indicators above, so that avoidable mistakes do not get repeated.

Cohesive Functionality

When I say that anything that is not a monolith is not too big, that’s not the entire story. A service and the services it provides should make conceptual sense; it should be functionally cohesive. It should not be a grab bag of random functionality shoved into the one repository. Grouping related things together increases the potential for reuse within the codebase, and makes it easier for external teams to discover developed features.

Determining where a piece of functionality belongs is one of the trickier parts of software engineering. Entire careers of software architects are consumed by decisions of where to put the code. A microservice gets around this uncomfortable situation because it only does the one thing. It is the thing, and it lives as a thing in its own right. The decision becomes about which clients get coupled to the microservice, not what belongs inside the microservice.

Thinking about your service as a product can help here. A product will solve problems in a particular domain for clients. That domain will act as a guide for what features the service should support. Ideally a product will have a strong product owner, and they will drive what gets built based on a understanding of what customers want and the underlying domain.

The Comfortable Team

The most compelling reason for reducing a service’s size is that the team in charge is not comfortable in managing the development of the service at that size. If the team’s senior developers feel they can’t control the quality of the code changes coming in, it won’t be long before the red monolithic indicators start flashing. This is not necessarily about size, more about growth. If a codebase is growing quickly in an uncontrolled manner with little technical oversight, it will become a monolith.

This problem is not about the size of the service, it’s about the size of the team. Think about the productivity of a ‘team’ of one. It’s very easy to stay productive, keep things clean and grow the codebase when you are the only one working on it. Issues start to arise when more people are added to the mix. People understand requirements differently. People have different coding standards and different ways to solve problems. People are familiar with different frameworks and patterns. The effort to get everyone onto the same page should not be underestimated.

So what is the right size for a team? There’s the idea of the two pizza teamwhich is nice, but pizzas come in many shapes and sizes. I think a team should grow to the maximum size it can where the teammates still feel comfortable (another soft term, I know). A team should challenge itself to grow as large as it can, and view it as a badge of honour if they can support a large size. As developers we love solving challenging technical problems, but are sometimes less keen on the human interaction side of our job.

Being part of a large, productive team working on big problems in a controlled manner should be seen as the pinnacle of our profession. We should aspire to that.

The Sensible Size

Comfortable teams and sensibly sized services! Companies like Google and Netflix aim for a single codebase across the entire organisation. But for the rest of us, here’s my easy recipe for sizing things just right:

  • Think of a service as a product, and allow the product’s domain to define what belongs inside the service and what does not.
  • One team owns that product, and that team should be allowed to grow as large as they feel comfortable with.
  • A team should concentrate on controlling growth rather than restricting size.
  • The product should be developed in one codebase, and the team always controls what merges and what does not.
  • Monitor the monolithic indicators and take appropriate steps if they show up, and finally,
  • Let your service grow into a beautiful, sensible size.

This blog post was originally published here: https://medium.com/@tom.kidman/sensibly-sized-services-part-2-5d13a333b75e