[ $davids.sh ] — david shekunts blog

☠️ Splitting microservices by "responsibility" is the biggest mistake ☠️

# [ $davids.sh ] · message #244

☠️ Splitting microservices by "responsibility" is the biggest mistake ☠️

Every damn speaker at a conference says: "split microservices by responsibility" – and every second one shares how they completely screwed it up.

#microservice #monolith #distmon

  • @ [ $davids.sh ] · # 1480

    Highlighting one of the most important ideas from a previous post and providing a concrete checklist that will help make an objective decision about allocating a separate microservice/module.

    Humans are very bad at classification and categorization, and if your methodology requires strict grouping (e.g., OOP or "separation of concerns"), you will never be able to do it correctly.

    Why? Because even if you manage to isolate a specific set of features by responsibility and break them down into microservices/classes that meet business needs, the world doesn't stand still. New requirements will constantly emerge that will very, very significantly blur the boundaries of this "responsibility," and what is the right architecture today will become the wrong one tomorrow, and not through your fault.

    Well, and of course, the second problem: "responsibility" is a very subjective concept. Ask several people in a mid-to-large system: "What responsibilities would you identify?" – each person's answer will differ by 50% from another's. And increasing the degree of "subjectivity" is a path to endless mistakes.

    And anyone who has divided microservices by responsibility in a mid-to-large project has ultimately encountered them calling each other millions of times and creating deadlocks.

    . How to divide correctly

    First, let's clarify: a "microservice" has a completely separate codebase and resources and communicates using inter-service transport, while a "module" (of a modular monolith) reuses the codebase and resources of another module and can directly call the code of a neighboring module (without transport), but exists as a separate application.

    This "forced responsibility" can be called "artificial responsibility" by developers, but we need to focus on "natural responsibility," and here's a checklist of examples of this "natural responsibility":

    . Separate Team

    If there are two teams of people who need to solve independent parts of the system and do not communicate on a regular basis (weekly, daily, their own management, etc.), it's better for each to manage their own set of microservices/modules and agree on APIs.

    Even shared libraries are dangerous (there should be 1 specific maintainer for this library), because any code intervention by a developer from an external team is poorly controlled and can degrade half the system.

    . Security

    You are building a module that handles transactions and want as few developers as possible not just to develop it or have access, but even to see the code (to reduce potential hacking).

    Then you isolate it into a separate microservice with its own repository and expose only a secure API.

    (continued below)

  • @ [ $davids.sh ] · # 1481

    . Geolocation Dependency

    For example, you have a task to collect data from your client's devices, but these devices are located within the internal network of a specific warehouse. In this case, you would allocate a separate microservice that will reside on a server within that warehouse and communicate with it via a transport layer.

    The same applies if you need to deploy a service only within one of the regions (e.g., only RU or EU); then you can create a separate module solely for that region.

    . Stateful

    For instance, you need to maintain open Web Socket or TCP connections, and you want to redeploy and generally touch this application as little as possible for maximum uptime. In this scenario, you would allocate a separate microservice/module that will store the state (sockets) and communicate with it via a transport layer.

    . Separate Feature Set

    Your partners require a stripped-down/different variation of your main API. In this case, you would create a separate module where you expose only what's necessary, plus replace the authentication method (e.g., with oAuth), and so on.

    . Dedicated Resources

    Your application requires extensive file system operations, or, for example, image compression, streaming video processing, or CPU-intensive calculations – in short, something that demands specific resources. Then, you would allocate a microservice/module and deploy it on machines with the necessary resources.

    . Interfering with Others

    A particular endpoint is exceptionally busy (e.g., you collect every click from a client's browser), and it generates too much traffic, the processing of which interferes with the processing of other requests.

    In this situation, you can isolate precisely this piece into a separate module/microservice and deploy it on a separate machine.

    . Independent Deployment

    **When we want different services to be deployable independently of each other (e.g., so that a redeploy doesn't affect applications whose code hasn't been changed in a PR).

    Then you can allocate a module (modules have their own independent Docker images, so after building, you can check if the hash has changed to decide on a redeploy) or a microservice.

    . Independent Business Logic

    **This is the MOST complex aspect because the independence of one microservice from another is often a temporary concept (remember that requirements always change). However, if one module is truly capable of operating independently of another (e.g., a reward calculation system and an authentication system), then it's a candidate for extraction into a microservice.

    BUT I recommend doing this as a second step, after you've realized that these modules have genuinely become independent of each other and for the convenience of further development, you want to separate the codebases.

    ONCE AGAIN, only time will reveal the true independence of codebases.

    Summary

    I'm sure I've missed other valid reasons for dividing applications into modules and microservices, so please share them in the comments.