This is a long overdue post, and I'm going to expedite it because otherwise I'll never post it. There's a heated debate going on right now about microservices vs. monoliths and I'm here to introduce you my answer.
BEHOLD. THE PRAGMALITH.
What is a Pragmalith?
A Pragmalith is boring. It's a way to build your app that embraces your ability to fuck up and tries to minimize the impact of your mistakes. It also recognizes that discipline is hard, so why not force good practices on us?
The rules of the Pragmalith
1. Choose the familiar rather than the exciting
Pick the edge cases and bugs that you know rather than having to discover new ones. Treat innovation like a rare and precious gem. Read Choose Boring Technology and pass it around.
2. Don't trust yourself
Document things. Write comments. Write tests. Don't SQL your prod database. Put safeguards in place to save you from the day you'll be too tired to care about discipline.
3. Don't be clever
Do things the boring way. Clever code won't save you CPU cycles but it will kill many brain cells — including yours when you'll come back to it after a year.
(It may also cause hair losses via forced pull action).
4. Split your frontend from your backend
Backends and frontend have different lifecycles. Keep them separate to minize risks. It will also prevent some spaghetti shortcuts where build backend logic in your views.
This here is the only rule where I'm calling for something specific. And this is coming from my experience. I worked as a dev and as a product manager and I'm absolutely convinced that separating the frontend cycle from the backend cycle works best for everyone (I can expand more on this if you really want to — just ask in the comments).
5. The default answer to creating a new service should be "No"
If you think you need a new microservice then you probably don't need it. New services or repositories should be painkillers, not vitamins.
E👏v👏e👏r👏y👏 new service will add a new set of 👏pro👏blems. Testing, deploying, backing up, testing the backups... Adding more services is expensive and brutal. And it should be weighed against a real pain felt today (slow release cycles, devs stepping on each other, etc...)
Architecture vs. practices
A Pragmalith is not just about how you structure your services. It's about having a certain mindset where you try to minimize headaches and avoid unnecessary complexity.
Do the things you have to, not the things you want to.
Why not a Monolith?
Monoliths have their own problems at scale and you'll need to break them up at some point. But really, it's about being pragmatic and do things that work for you.
I don't trust myself to have the discipline to avoid shortcuts in the code. That's why our backend is a REST API in a separate codebase: it has clear inputs and outputs, and you can't cheat in the views. The separation of concerns is forced on us.
What's our Pragmalith at Tability?
- Rails 5 API backend with Postgres
- React* for the web app frontend
- React Native* for the mobile app
- Gatsby for the website
- A Rails microservice for the Jira Connect App
* Items with a star mean an innovation token was used at the time
React was a new framework at the time, thus being an expensive innovation token to use. BUT, it was going to remove a lot of pain for our small team. We could re-use a lot of the web app code for the mobile app, and once I got the hand of React it became quite easy to approach Gatsby.
But all the other stuff is boring. I really want to try Elixir and Phoenix, to learn more about Kubes, to explore all that AWS has to offer. But it would kill us.
We'd have an exciting tech but a boring product.
The Pragmalith hates fights
A Pragmalith is something that works for you and your org. The threshold of what's boring moves with the competencies and experience of the team.
I'm out ✌️
Top comments (5)
"I don't trust myself to have the discipline to avoid shortcuts in the code. "
I would suggest to build it. That's great to have a REST API, but it makes everything a bit more complex than having a monolith. You have more to do on the network side of things. Plus, REST is great for CRUD, but might get messy when you want to be "action driven". RPC might be better suited for that. Which brings another technology and more complexity.
On top of that, you can still have shortcuts everywhere in your backend. Bring domain logic in your handlers and you'll quickly see what I mean.
I don't say that your approach is bad. I don't say that monolith are always good. I say that we need to be disciplined.
Everything should be contextual to the team. In our case REST made much more sense than any other approach. For a different team and a different app it could have been different. Another thing to consider once again is the cost of learning something else — it really has to bring huge benefits to be justified. All I'm saying is that you need to pick the approach that takes care of most of your problems rather than relying on devs to maintain a high level of discipline. Another example of that can be seen in the use of static types.
I've also seen many different approaches to software dev come and go but one of the core principles has always been to find systematic ways to reduce risks. That's why we do code reviews, automate tests and deployments, have immutable infrastructures. Everyone is, of course, doing their best, but it's so much better to know that safeguards are built within the way you work. "Don't trust yourself" is not meant to say you're bad at your job, but rather that bad days can happen.
Hope that makes more sense.
I agree with you. Even if they do their best, developers make mistake. That's why we have many, many tools to prevent them. It makes sense, development is hard, because we have these huge codebases we need to put in our head and try to find reasonable solution.
However, if you "don't trust yourself to have the discipline to avoid shortcut in your code", do you really do your best? Making mistakes imply that you don't know the mistake you do; if you know it, then don't do it, or find a way not doing it. What I understand with your sentence is: I know I'm coding a shortcut, it's bad, but I don't have the discipline to bring a better solution. The mental process here seems wrong to me.
Don't get me wrong: I made shortcuts, and I will do, again. But I don't want to say that it's normal and I was right. Even if the context pressured me to do it. I should simply acknowledge my mistake, and try not doing it in the future. It shouldn't even less explain why I choose a technology instead of another.
On your choice for REST APIs, I think you should choose the good tool to solve your specific problem, not choosing the technology because everybody knows it. We are developers, we can learn. If we can't, we will have quite some problems soon, when we'll need to adapt to some new technology / language / whatever.
I don't say that your choice of REST APIs don't solve your problem. I don't know, maybe. What I say is, for me, your reasoning is dangerous.
As an example, I had a technical leader once who said that modern JS frameworks were too complicated, and we should use what everybody know: jQuery.
It was a mess. The Frontend was way too complex for the poor jQuery. The developers in love with it left, and they couldn't find anybody who wanted to code jQuery. A total disaster.
I see the point you're making and I don't think it goes against what I've said. I've never said that you should only pick things you're familiar with: case in point with using React/React Native at the time. This decision was based on what it could help us do rather than what we're familiar with. But it's surely dangerous to try to innovate in all aspects of your codebase and infrastructure. Don't focus on the choice of REST, but rather look at all the decisions we had to make at the time.
That's risk assessment 101. In every org I've been (ranging from small startup to post-IPO) the consensus is that you should provide safeguards, no matter how experienced people are, and especially as you grow. I feel like you're taking my point as an attack on competencies — it is not. People get tired, bored, or have to work under pressure at times. Good risk management takes that into account. In our case, we're currently a team of 2 having to juggle multiple hats (devs, marketing, ops...) and the less I have to think about how to build things, the better. Once again it's contextual and YMMV.
TL;DR; I don't think we're in so much disagreement.
I agree. At the end it's more the way you say it which bothered me, more than what you meant by it.