The Pain of Maintaining a PHP Web Application
Maintaining apps, in general, can be a headache, but PHP seems to take the crown when it comes to abandoned projects. You’d be surprised how many PHP apps are still running outdated versions of Laravel, CodeIgniter, or even pure PHP scripts written in a style that should have been buried decades ago.
A client once told me, "If you can buy it, why build it?" Another said, "Use a library and modify it as needed. Don’t build from scratch!" We’ve all heard the wisdom of not reinventing the wheel, and yes, dependencies help us move faster. But sometimes, they come back to haunt us.
I’m here to tell you—if you have the time and resources, build your system from scratch! It might be the only thing you’ve ever done right.
The Horror of Legacy PHP Codebases
Ever dealt with an old PHP codebase? If you haven’t, consider yourself lucky. I’ve been hired multiple times to work on some truly cursed projects—ones that make you question the life choices that led you to become a developer.
One project, in particular, holds a special place in my heart (for all the wrong reasons). The app relied on about 48 third-party packages, most of which were either:
- Outdated
- No longer maintained
- Missing documentation
- Completely vanished from the internet
Trying to update the app was nearly impossible. Replacing packages wasn’t straightforward because many of them had no direct alternatives, and some repos had been deleted entirely, meaning we couldn't even fork them to maintain our own versions.
My rational suggestion? Rebuild it from scratch.
The client's response? "That’s too much work. Can’t we just patch it?"
They proposed an alternative: replacing each failing dependency with a third-party service. Soon, we had an authentication service, email service, file storage service, admin, and more. In theory, this seemed like a good idea—until the challenge became how to connect everything efficiently.
Enter the API Gateway
An API Gateway is a central point that manages, routes, and secures API requests to various services. Think of it as a traffic cop standing at the intersection of your microservices.
Instead of the frontend calling multiple services directly, everything goes through the API Gateway, which:
✅ Authenticates requests
✅ Logs activity for monitoring and debugging
✅ Applies rate-limiting to prevent abuse
✅ Converts responses into a consistent format
✅ Routes traffic dynamically
This turned out to be a game-changer for the project. Instead of spaghetti code with random HTTP calls scattered everywhere, all external and internal services now went through a single controlled entry point.
Quick Example of API Gateway in Action
class ApiGateway
{
private $services = [
'auth' => 'https://auth.example.com',
'storage' => 'https://storage.example.com',
];
public function handleRequest($service, $endpoint, $params) {
if (!isset($this->services[$service])) {
throw new Exception("Service not found");
}
$url = $this->services[$service] . $endpoint;
return file_get_contents($url . '?' . http_build_query($params));
}
}
This simple example demonstrates how an API Gateway can centralize service requests, making an application more manageable.
The Double-Edged Sword of Frameworks and Libraries
At this point, you might be thinking, "Nixx, are you saying we should never use frameworks or libraries?" No, I’m not! I love frameworks, but you have to use them wisely.
Advantages of Using Frameworks and Libraries
🔹 Faster Development: Instead of writing authentication, routing, or database abstraction from scratch, you can leverage existing solutions.
🔹 Security Best Practices: Well-maintained frameworks (like Laravel or Symfony) come with built-in security features like CSRF protection, input validation, and secure password hashing.
🔹 Community Support & Documentation: If you get stuck, chances are someone else has already faced (and solved) the same problem.
🔹 Scalability & Maintainability: A good framework provides a structured way to grow an application, making it easier for new developers to onboard.
🔹 Avoiding Repetitive Work: Why reinvent the wheel when someone has already written a battle-tested version?
But here’s the catch...
The Drawbacks of Relying Too Much on Frameworks and Libraries
⚠ Dependency Hell: When your project is glued together by dozens of libraries, a single outdated package can break everything.
⚠ Performance Overhead: Some frameworks and libraries come with extra baggage that can slow down your application.
⚠ Security Risks: Using third-party packages means you’re relying on someone else’s code. If they abandon the project or introduce vulnerabilities, your app is at risk.
⚠ Breaking Changes: Ever updated a library only to find that half your code no longer works? Framework updates can introduce breaking changes that require massive refactoring.
⚠ Loss of Control: If a core library you depend on is discontinued, your entire project could be in jeopardy.
The Sweet Spot: Smart Development Strategies
So, should you build from scratch or rely on libraries? The answer is somewhere in between:
1️⃣ Use well-maintained frameworks but understand what they do under the hood. Don’t just install a package and hope for the best.
2️⃣ Limit dependencies to what’s truly necessary. Just because a package exists doesn’t mean you need it.
3️⃣ Write core business logic yourself. If something is critical to your app’s functionality, don’t delegate it to a third-party library that may disappear tomorrow.
4️⃣ Keep an eye on updates. Follow the maintainers of key libraries and subscribe to release notes.
5️⃣ Build with future-proofing in mind. Assume that at some point, you might have to replace a library or even rebuild your app. Design with flexibility.
Final Thoughts: To Build or Not to Build?
If you’re working on a small MVP, use frameworks and libraries to move quickly.
If you’re building a long-term product, be mindful of dependencies and consider writing core functionality yourself.
There’s no universal rule, but the more control you have over your code, the better prepared you’ll be for the future.
And if you ever find yourself debugging an ancient PHP project held together by abandoned libraries, may the odds be ever in your favor.
Stay Connected!
For more tutorials, tips, and insights, follow me here:
🔗 GitHub – Explore my projects.
🎥 YouTube – Subscribe for in-depth guides.
💼 LinkedIn – Let’s connect professionally.
Let’s keep building and learning together! ✌️❤️
Top comments (1)
Great post!
An authentication package that is out of data that is scary. Hopefully they used newest code when they still developed the package.
Outdated and not maintained packages are not the end of the world is the quality of the code is good. Then it is just a question of are we willing to maintain the code or not.
If there were no repositories for some of the packages, I guess it wasn't a composer based project? Or one where they added the dependencies to the project.
I think rebuilding from scratch is a thing we as developers want to do too often. When I'm thinking rebuild, I take a breath and then I check how much can be saved. And try to find out ruff timelines to get the project back on its feet.
As an advantage of frameworks and libraries I would add, getting expert knowledge in the form of a package. People that are dedicated to building one thing are going to have more knowledge on that subject.
As an extra the security drawback I would add supply chain attacks. It is sad but they seem to happen more and more.
While I agree with the smart development strategies, I want to add some nuances.
Writing core business logic yourself.
Yes in most cases, but if there is a package that passes the good to use test. I would use it. Rewriting a package is just spending time in my opinion.
Build with future proofing in mind
It is easy to go too far with this, for instance creating a lot of abstractions that aren't needed.
I think with interfaces and composition you already have a lot of flexibility.
My worst legacy project was a old Nova driven site where they build a custom CMS on top of, with no documentation and forty plus packages.
Only figuring out how the packages interacted with each other took me two days.
The only thing going for it was that it worked.
Changes took a lot longer than they should have, so most of the times we tried to come up with alternatives. But we had to say no to some more complex changes, because those would be too much effort to get the end result.
It was fun in the sense that we had to be very creative. And when we could incorporate changes that got us closer to an updateable codebase It felt like we were closer to the top of the mountain.
While we did moan and curse with legacy projects, we also learned a lot.