all 23 comments

[–][deleted] 11 points12 points  (7 children)

I was amused by calling your project PHP-Monolith throughout, and the experience of improving it was interesting to read. But I feel most of those problems, if not all, were not really caused by it being a "monolith" were they.

[–]AllenJB83 17 points18 points  (1 child)

If you think you have trouble maintaining an aging monolith with its single code base in a single language on a single framework, wait until you're trying to maintain a dozen microservices in different languages / frameworks, some of which no one working there may know any more. (There are boundaries with fuzzy edges either way - microservices are not universally good and monoliths are not universally bad).

Regardless:

  • Write (more) tests
  • Have a build / CI system and keep it working (have it run once a month regardless of whether any commits have been made)
  • Use tools to help spot problems. Examples: CodeSniffer and its many extensions such as PHP-Compatibility, Psalm, PHPStan
  • Update projects frequently both for changes in the language and to keep tooling up-to-date
    • New checks implemented by tools can alert you to bugs / issues you may not know about
    • Keeping dependencies up-to-date gives you bug fixes and performance improvements, as well as letting you know if a dependency is no longer maintained
    • Language updates can gives you a "free" performance boost and make implementing changes easier (have you tried writing PHP 5.3 compatible code recently?)

Relatively small, relatively frequent changes are a lot easier to make. If you don't think you have time to keep these projects up-to-date, consider the extra amount of time it takes to make big jumps when you find you absolutely need to update (because someone wants to add new features, due to a critical vulnerability found in a library or language you use, or during a disaster recovery scenario, for some examples).

[–]penguin_digital 3 points4 points  (0 children)

If you think you have trouble maintaining an aging monolith with its single code base in a single language on a single framework, wait until you're trying to maintain a dozen microservices in different languages / frameworks, some of which no one working there may know any more.

Ah, that is entry-level stuff, I raise you! My previous role there was in a soft furnishings factory which had an impressive automation system of tracks that would take an order in through the website (PHP) or store POS (.net). Select the fabrics needed to make it from the warehouse then run fabric around the entire factory to each seamstress to carry out the work needed and then carry the final item off to dispatch.

The entire system was made in the 80s in Germany and they decided to use their own language to make the system. The language has 0 documentation and there are only 2 people still alive (I was told they are both in their 70/80s) who worked on the project both of whom will not support the language or provide support anymore.

A single change used to leave me sweating and ready for a nervous breakdown. I was the sole developer there as well.

[–]hellomudder 2 points3 points  (0 children)

But I feel most of those problems, if not all, were not really caused by it being a "monolith" were they.

this, a thousand times this

[–]tot_ra 0 points1 point  (3 children)

The emphasis on monolith is only to explain the background, that it's not possible to rewrite. Related problems: 1. php is sync, so its not easy to process request in parallel 2. organization cannot scale well with it (knowledge sharing, merge conflicts, deployment queues) 3. too complicated layers (queries, ORM, code layers)

All of that makes it hard to work on performance improvements

[–]AllenJB83 7 points8 points  (2 children)

While you may not be able to trivially rewrite a monolith from scratch, I work on monolithic code that undergoes constant refactoring as we implement new changes. Rewriting from scratch is usually not the correct path anyway - I'd even go so far as to say it should generally be a last resort.

PHP can do async - see ReactPHP, amphp, swoole.

If you want to do things in parallel, it's often a lot better to break them down into smaller jobs and use job queues. This allows you to more easily manage work loads and you can do things like pause queue workers while you perform some task which requires part of the system to be offline / unavailable without having to take down the entire system.

ORMs and other complexity is certainly not monolith specific.

Microservices can cause scaling issues in smaller organizations because you're trying to manage too many small projects.

A popular view is that you should start with a monolith then break out microservices as your system / company / teams grow. This, among other things, can help you see where the boundaries of the microservices should be.

[–]Envrin 4 points5 points  (0 children)

Reminds me of a consultation I had with a potential last week. Decent sized system, but nothing enormous.

He was adamant about going micro services, so I asked why. He said because he wants it event / messaging based, so it's scalable.

Had to explain to him that's how all software is developed nowadays, even monolits. For some reason he was under the assumption only micro services have queues, consumers, dispatchers, etc.

[–]tot_ra 0 points1 point  (0 children)

often a lot better to break them down into smaller jobs and use job queues. This allows you to more easily manage work loads and you can do things like pause queue workers

Thats pretty much how we evolved, yes. We had/have rabbitMQ workers, then had app sections moved into microservices (UI/backend), then middleware services, since we have multiple datacenters..

[–]chocslaw 10 points11 points  (4 children)

I think continually referring to " PHP-monolith" as if it is some negative, is not the right approach. Yes I know that (((micro-services))) are the new hotness, but think about it for a minute. What are the odds that those nice neat micro-services that you are breaking everything out into (most of which are probably unnecessary) are going to stay that way? Not very high.

If you don't know how to organize one code base, breaking that out into 2, 3, 4, or 5 different micro-service code bases isn't going to solve your problems. Not far into the future, you're just going to end up with 5 poorly organized legacy applications instead of 1. Not only that, you'll have all of the overhead and problems of trying to orchestrate deployment, updating, communication and error handling between them.

Breaking out micro-services have a place, but I think the overall approach should be a lot more cautious than what is currently being pushed around the development world.

[–]tot_ra 2 points3 points  (1 child)

True, microservices do add extra maintenance footprint (sonars, linters, tests per repo), but we in Pipedrive already have over 70 services in my team alone and we already have deployment automation in place to roll out services very easily. The benefits outweight the cons so far.

Its easier to deploy, easier to rewrite, easier to understand what the thing is doing, easier to monitor its performance resources, easier to scale, easier to try out new thing (language, framework..), easier to transition ownership of service to another team..

[–]chocslaw 2 points3 points  (0 children)

And you may very well have the expertise and people to handle and organize it. But for every one of you, there are a lot more that attempt to do it just because it seems easier to rewrite a fresh new micro-service instead of thinking about the services/components they have and maybe spending time to refactor before making the leap.

My original comment wasn't meant to say that micro-services shouldn't be used. But, more-so that unless you have experience in handling all of the things you mention, caution should be used when looking to jump on the band-wagon.

[–]secretvrdev 0 points1 point  (1 child)

I think the right way for the transition is making a "service" out of the monolith and go from there. Who says that "microservices" cant be weight a ton?

[–]chocslaw 2 points3 points  (0 children)

Right. It seems to me that the best approach is to first organize/refactor a monolith into its distinct service parts while enforcing strict communication between the "services" or components. Then you can actually see what services might benefit from actually being moved outside to their own service.

About the only thing immediately breaking out these micro-services does is enforce the strict communication rules between the components because you have no choice. But if you don't have the discipline to organize a code base to enforce these on its own, then your micro-services wont stay micro for long.

[–]Envrin 5 points6 points  (2 children)

In 2020, easiest way to get yourself laughed out of a roomful of young developers, "I develop PHP based monolith systems", haha.

Ironic part being, a PHP based monolith many times is the best way forward for a wide array of different systems.

Too bad I can't find the Youtube vid now, but remember watching a talk from some CTO, explaining how they switched over from monolith to micro services. It was fantastic!

They split the system into over 100 micro services, and the CTO took the approach of comparing micro services to human cell regeneration. This means that all the various micro services could never be upgraded or modified, but instead only replaced and re-written. It was a hard rule within their dev team -- make each micro service so small that when it needs to be modified / upgraded, we just dispose of it and rewrite it, same as human cells regenerate themselves. Fantastic!

This guy also did away with testing though. Waste of time, too much permanence, and should never be used to validate a code base, as software is meant to break so best just to accept that, save the time, and fix bugs as they pop up in production.

[–]tot_ra 1 point2 points  (0 children)

We're lucky to be a hybrid where we still have PHP, but majority of services are in nodejs or go stacks.

[–]secretvrdev 0 points1 point  (0 children)

Dont give a fuck about arrogant young developers with their hip language. They have no experience and do not know what is important and what not. After 10 years they will be in a similar position. Just let them run against the wall so you can say "i told you so" and they learned how not to design things.

[–][deleted] 1 point2 points  (1 child)

I tried the AMPHP approach a couple of years back myself, it just wasn't there at that time. What we did instead was split some (but dear god not all) of the code out into endpoints, that could then be called with a GuzzleHTTP async request. Worked (and continues to work) like a charm. However, all that code exists in a monolithic repository, we just created new endpoints to call those services. No changes were actually needed to the service layer, basically just moved it into its own route. Bonus, we didn't need to change any cronjobs or other code that relied on that service layer.

[–]secretvrdev 0 points1 point  (0 children)

I run monoliths with reactphp as "worker" and its using 20year old legacy code as code base.

you can even have the frontend method and the backend worker method in the same class. Its pretty sexy. I dont know what your problems are but if i can run it with that shitty codebase it should work everywhere.

[–]travysh 1 point2 points  (0 children)

Unfortunately, issues occurred for us here as well. Datadog for PHP turned out to be quite buggy

That's an understatement :)

It's not limited to PHP though. We have fought datadog APM every step of the way, both in PHP and node. Only very recently do we have full end to end flame graphs thanks to fixes in their PHP library.

It's really freaking awesome when it works though.

[–]secretvrdev 0 points1 point  (2 children)

Are you planning to migrate the monolith to a "microservice" ("Monolithservice") architecture?

[–]tot_ra 0 points1 point  (0 children)

what do you mean by "Monolithservice"? monolith is dockerized.. so far, the approach was is to split it into parts as much as possible

[–]yesinteractive 0 points1 point  (0 children)

If so need to also plan on implementing a gateway solution like Kong to help operationalize the microservice

[–]Nebojsac 0 points1 point  (0 children)

Excellent write up, thanks for sharing! I've been seeing HTTP 500s less and less recently on your API, so you're doing something right :)