Hacker News new | ask | show | jobs
by grandalf 4052 days ago
What is a monolith? Intentionally tightly coupled code that you feel is going to be manageable in the future?

I think the biggest anti-pattern is "DRY". This turns a simple rails view into a monstrosity that is used to render every page of a site, or a class into a self-referencing recursing mind-puzzle.

When a codebase starts down the monolithic path, developers will often use the DRY anti-pattern to "shrink" what is actually complexity creep. They will also use another anti-pattern which is using the test coverage percentage as a key metric for deciding what to build/improve.

Both of these are attempts to fix something that is a far bigger problem: excessive, unnecessary coupling in the core design. This is often because (as others have pointed out) of blind adherence to frameworks and framework "best practices".

In my opinion, the most important metrics are coupling, lines of code (the fewer the better) and readability/understandability. Every line of code that you maintain ads a cost to changing/improving the codebase (because someone has to understand it in order to change it).

If you think of code in terms of its consumers, nearly every API is a micro-service. The less magic/faith required by the consumer to use the API, the more effectively it can be used and changed.

1 comments

There are two ways to think about microservices. Development (which your comment focuses on) and deployment. The pure/idealized microservice is isolated from the rest of your environment/codebase on both of those axes. In practice, there will be some coupling on both, and perhaps one of those isn't isolated at all.

The monolith is simply the opposite of the microservice ideal, it is heavily coupled in both development and deployment.

And yes, DRY does often run counter to the development isolation, as you need to balance repeating yourself or sharing code (and reducing isolation).

As these things generally go, any system in contact with the real world will exist somewhere between the monolith and a federation of microservices. My current company, for example, employs 2 distinct codebases, Java and JavaScript, which have 3 and 2 deployments respectively, each in two similar-but-not-the-same environments (in-house and white-label). We could easily split those deployments into twice or 5x the number, we could also join them, this just happens to be where things naturally settled for the time being.

There's an additional aspect... Types of load (compute vs i/o)... I'm currently building a system where node/iojs is the primary application runtime. In this case, separating those portions of the application that are compute heavy (resizing images, recalculating values, etc) are offloaded, beyond that I have separated the backend data into discrete containers based on collections of data/types... that which is related to an account, vs that which is related to a user/profile (and authentication)... which makes a bit more sense. I also separate the authorization as a service layer above backing logic.

UI (shared client/server codebase), WebAPI (ui access, auth filtering), DataAPI (used from WebAPI), Profile Service (users/auth), Account Service (core data), Image Service (backs upload/resize) and various backing pieces... for me I didn't break things up into too fine grained pieces, but wanted breaks based on type of work and/or data.

It really just depends on your workload.

Makes sense. I typically try to design things so that they are initially joined (for convenience) but can easily be split off -- such as a simple router between micro-apps which can be replaced by haproxy (or similar) later.
I like this too and usually do the same in the application layer for new projects (though it takes more discipline).

Every module is built with the intent of breaking away later. Disallowing intra-module communication is essential to this. That means no global ORMs or global anything really.