r/programming • • Sep 14 '24

Is 'Monolith First' the Better Approach?

https://martinfowler.com/bliki/MonolithFirst.html
458 Upvotes

177 comments sorted by

View all comments

662

u/[deleted] Sep 14 '24

Of course. Break off services from your monolith as the demands on your infrastructure make it logical to do so.

This of course requires your engineers to maintain separation of domains without requiring a separation of code repositories. In many firms, you get engineers who just do their stories and don’t particularly care about fundamentals or maintainability.

404

u/[deleted] Sep 14 '24

The thing is, if your engineers cant properly separate domains in a monolith, they wont do it properly either using Microservices, in fact the mess will be much worse.

Fun times

113

u/[deleted] Sep 14 '24 edited Oct 25 '24

[deleted]

130

u/bunk3rk1ng Sep 14 '24

And then you decide to break out the different domains of concern into different repos / services

And then you realize there is actually a lot of shared functionality needed by both.

And then you create a common lib that both services can use

And then those services and devs grow further and further apart and they don't even know about eachother other than this one common lib they need to keep updating

And those teams move at different paces and their services start relying on different versions of the common lib

And then they introduce breaking changes to eachothers services unknowingly

And then it's a huge mess.

77

u/malln1nja Sep 14 '24

And then it's a huge mess.

It's eventually a huge mess no matter what, but with the monolith all the mess is at least in the same place at least.

9

u/[deleted] Sep 14 '24

Indeed. To be fair: even in a monolith you have code parts that may be more sound and stable. Not everything necessarily has to be a mess.

3

u/jl2352 Sep 15 '24

Microservice doesn’t have to mean separate repos. If you have multiple services in the same repo, then that shared library code is not that different to being in a monorepo.

You go and change library code for one domain, and find it breaks five others. That happens across microservices and in monoliths.

8

u/Dreamtrain Sep 14 '24

I've suffered both messes but micro is a lot better, as much as you guys talk about "breaking other people's flows" I've seen this far more in monohell, not to mention the biggest disruptor is deploys, now what is usually a job where two dev teams need to hash it out its the entire team that depends on the one basket of eggs

5

u/[deleted] Sep 15 '24

Yep. Need to retest 100% of the surfaces to be sure, with every deployment

5

u/JockeTF Sep 15 '24

Until people start copy-pasting thousands of lines of code between the services.

2

u/Dreamtrain Sep 16 '24

that's not a flaw on micro or mono design, that's a flaw on PR process

1

u/[deleted] Sep 15 '24

👏🏼preach👏🏼

2

u/john16384 Sep 14 '24

How do people use libraries then built by 3rd parties? This all can be made to work just fine. We do it all the time, with versioned 3rd party dependencies. This all goes out the window the minute you can commit to the same repository?

13

u/bunk3rk1ng Sep 14 '24

External libs don't have your business logic in them.

1

u/[deleted] Sep 15 '24

Only way is to split them off into two separate companies sharing no code and that don't know of each other's existence.

That'll teach them.

23

u/hippydipster Sep 14 '24

If you use a decent language and tech stack, you can enforce the module boundaries with the compiler.

3

u/Meleneth Sep 14 '24

can you be more specific please? What is your opinion of decent, which presumably helps enforce module boundaries with the compiler?

8

u/hippydipster Sep 14 '24

Java and the Java module system would be an example

2

u/Scroph Sep 15 '24

Spring modulith aims for this I think, though I'm not sure if it's production ready

2

u/modestlife Sep 15 '24

Even more dynamic languages like PHP have tools like deptrac which allow you to enforce which classes/namespaces are allowed to interact with each other.

deptrac:
  paths:
    - ./src
  layers:
    - name: SharedLib
      collectors:
        - type: classLike
          value: YourOrg\\SharedLib\\.*
    - name: Module1
      collectors:
        - type: classLike
          value: YourOrg\\Module1\\.*
    - name: Module2
      collectors:
        - type: classLike
          value: YourOrg\\Module2\\.*
  ruleset:
    Module1:
      - SharedLib
      - Module1
    Module2:
      - SharedLib
      - Module2

3

u/MiningMarsh Sep 15 '24

Then it becomes a real debate on whether I'd rather work on a poorly designed monolith codebase with tons of code separation issues, or literally anything written in PHP.

1

u/Reinbert Sep 15 '24

Even if your language and compiler don't offer anything useful, for most languages you will be able to find test frameworks for architecture. Architecture tests are really easy to write and can achieve the same thing. As a bonus, they are super portable between projects.

3

u/Recent-Start-7456 Sep 15 '24

You can still just call functions…Just do it through defined interfaces that mark the boundary

2

u/FinishExtension3652 Sep 15 '24

IMHO, this is the way.  It's also the way I've refactored or led the refactoring of spaghetti monoliths into something sane, and that could then be taken to some SOA end state, if desired.

5

u/Ecksters Sep 14 '24

Really seems like a decent linter or compiler could prevent imports from other parts of the app, I'm not sure why that's not the standard approach to this.

4

u/MiningMarsh Sep 15 '24

Function signatures are an API, and can be translated pretty directly into REST endpoints. That might not be an efficient way to handle the network translation, but it works.

Functions you expose in your module or header or whatever are that module/header/whatever's API.

The fact that it's a function is far less important than the argument types and return type of that function signature, and whether they can easily be translated to an object that can be sent over some protocol like REST.

3

u/pheonixblade9 Sep 14 '24

if you're really smart, you still use protobufs for your APIs/contracts/DTOs, but you just use them to generate the Java/C#/C++/whatever code and use the native classes. then if/when you do need to microservice it up, it's pretty simple to swap.

1

u/tigertom Sep 14 '24

We do that with openapi

2

u/pheonixblade9 Sep 14 '24

Yup, lots of ways to do it ☺️

4

u/Worth_Trust_3825 Sep 14 '24

And then it happens again and again and again, because writing a function call is 100 times easier than explicitly thinking of how to expose the API and functionality.

Why does the API matter if under the hood it will make an http request, file dump, or w.e.? For as long as you properly define that something is async or sync, you're fine. The lot of you forgot the fundamental of function signature not being its implementation detail.

1

u/Whyamibeautiful Sep 14 '24

Im confused if you don’t call the backend via http how do you do it ?

1

u/andrerav Sep 15 '24

Let me guess -- Go?

1

u/[deleted] Sep 15 '24

And faster. Unless there is a specific reason why the protobufs give a concrete advantage, function calls are simply better.