Coming back to the monolith, Q&A

Sergi de Pablos
ulaboxeng

--

Yesterday we had the pleasure to present Coming back to the monolith at the PHP Barcelona 2019 conference through our VP of engineering Manel Sellés.

First of all we would like to thank to the organizers of the event — as specially Ricard Clau — for the opportunity given to us to present in an international event. It’s not easy to speak in front of so many of your peers, specially when it’s not in your native language. We would also like to thank them for bringing back the classic and sorely missed Barcelona PHP conference.

We would also like to think that our talk is a reality check of something needed by the Barcelona IT ecosystem. There are lots of small and medium sized teams with tons of legacy code that have doubts regarding going the microservices way or not.

Yes, we failed… but we also learned a few lessons among the way, lessons that we wanted to share with the community yesterday. We think that as developers dealing with unknown unknowns making mistakes is expected, but we also think we should be more transparent and open about them, so other teams can do better. “The only one that makes no mistakes is the one that never does anything”.

Question & Answers

Q: How do you manage to transform to a monolith when you have a ton of dark legacy code? (87 votes)

A: Exactly in the same way you would do it if you were moving to microservices, using the Strangler Pattern. Creating harder boundaries helps a lot here, the only difference is that instead of moving the code to a microservice, you move it to a module.

Q: That’s what happens when you build something based on hype instead of necessities… (73 votes)

A: We really had and still have those necessities, as proven by the fact that no one of the services created really disappeared, they were just integrated back into the monolith. It may look like it was a hype-based decision but it really wasn’t. As a team we try to be up to date on new techniques and tendencies regarding our areas of interest — programming design, architecture, good practices, devops… — and as some of these ideas become more and more popular not just because they're cool or trendy but because they make sense and offer solutions to real problems, problems we have, we try them. As usual, "it seemed like a good idea at the time". Of course, time is the one that makes us correct or fools, and we like to think that, at least, we learnt something on the way.

Q: What’s the best way to communicate between modules: calls to an API or call through PHP code? Any chance for command line? (64 votes)

A: Internal calls using the module API, no HTTP is involved here. CLIs are defined on whatever application we need and using a module is as easy as importing the module API class (it could be defined as a facade) and calling the desired method, that will execute the Command or Query against that module internals.

Q: Was one Bounded Context assigned only to one squad or everybody could touch everything when doing a feature? (60 votes)

A: We’re a small team, so even if the initial development of a new bounded context fell into the hands of two or three people, later on everyone can modify it.

Q: Do you think you implemented the ”distributed monolith” antipattern instead of actual microservices? (27 votes)

A: No. The services were connected asynchronously through messaging — RabbitMQ in our case — and were able to live independently from each other. As per explained in Microservices Ending up as a Distributed Monolith we didn’t have “shared libraries required to run services”. Manel explained that we tried to unify some helper libraries, but that was just a commodity, to avoid repeating some infrastructural code again and again.

Q: How can this be introduced to a team? It seems a step back according to current micro services hype. (17 votes)

A: Yes, it looks like a step back. The only thing that occurs to us is trying to create a simple feature both in a modular and microservice way, and see which one is faster to release to production. Also remember that nothing comes for free: you’re basically exchanging internal superfast calls with network ones, so we’re pretty sure you’ll see a performance improvement. As always, everything is relative and “it depends”; being aware of that is the key.

Q: Maybe is not about microservices but a wrong architectural decisions, don’t you think? (14 votes)

A: Yes and no. We took a few wrong decisions while defining the bounded contexts for our services, but our pains came from a different place: we didn’t have the people — 10% of the team as per Anthony Ferrara own words is his talk the day before ours — or the tooling to fully go the microservices way. We also didn’t establish a full ops protocol or some base rules regarding communication between services — we gave up uniformity in favor of flexibility and agility. Finally, as our monolith was the source of truth for a lot of tools — backoffice, reporting , integrations with external systems — every piece of data had to go back to the monolith (yes, we’ve learned that we should have tackled that problem first).

Q: So this is the result of the chaotic microservices you showed on the past talk… (10 votes)

Actually no. That service — our warehouse management system — is actually the only one that makes sense as a separate service — and will continue to be — because of its own SLA, it does not send every piece of data back to the monolith, and has been working without a glitch — and more important, without a single bug — for more that three years now.

Q: Three years ago, they showed a really chaotic microservice that nobody should even implement. Now they go back on the monolith when they would benefit more with a better designed microservice. 2 times 1 step forward and 2 step backwards. (6 votes)

A: See previous question.

Q: Can architectural constraints be automated to avoid mixing services for different modules? (6 votes)

A: They can, but we’re still not there. We think than the current PHP tooling (e.g. phpat) allows for this kind of checks, like checking for uses in classes only within the current module base namespace excluding the ones used for calling other modules APIs.

Q: How are you working with different version of modules and bc changes?

A: We don’t need different versions of modules. This is needed with microservices because you cannot — and should not — coordinate the deploy of multiple microservices at the same time, but remember we have a monolithic deployment. If we change the public API of a module we also change all its usages across the rest of modules.

Q: Is each module an entire application? E.g. a Symfony application.

A: No, it’s not. We have 3 different applications right now (web, api, backoffice) that load and use some bundles and modules: generic ones like “Core” and also specific ones, depending on the needs. What they have indeed is a specific Symfony configuration delegated, where they define the DI services on their own.

Q: How do you manage that all your engineers work in the same codebase with the monolith?

A: If you’re talking about conflicts because of working in the same codebase, probably short lived feature branches is the clear winner. We are continuously releasing to production, so unless two people touch the same class on the same place at the same time, we don’t have merge conflicts. Yes, we have a legacy monolith, but one that on average we’ve been architectural improving for years even if sometimes we did things wrong, so in general our codebase is already following SOLID principles.

Q: I was wondering how you handled the persistence during that migration though :) Was the database distributed too? Did you merge them back to the monolith?

A: Every service had its own database, and as soon as we were merging services into the modular monolith, we also merged the database, adding as many tables as necessary to the main database, to finally remove the service legacy database. In our case, as a lot of fields were already replicated in the monolith for reasons previously explained, the migration was not so painful.

What else?

As a disclaimer for everyone that after watching the talk thought “this devs don’t know what they’re doing”, ex-members of the team are currently working for companies like ThoughtWorks, Spotify, Free Now (MyTaxi), Ocado… coding in PHP, Elixir, Python, Java, Go… and they are still applying things they learnt or taught at Ulabox.

Among other things, we’re proud to have pushed better practices in the Barcelona PHP world since our inception. We promoted Symfony 2.0 before it was even released, we moved to PHP 7.0 as soon as possible, we started introducing DDD in 2015, we were doing CQRS even before we read about it for the first time, we have event sourced apps working in production without a glitch, we’ve had an automated CI/CD pipeline for years that deploys hundred of times per month — remember we’re only 10 guys .

What we’ve learnt is that technology is a tool, not a goal, and we should be focused 100% in delivering value for the company, and delivering it as fast as possible with as much quality as possible. For us this means mostly avoiding microservices, or at least avoiding them for now.

--

--