Liferay Upgrade vs Modernization: What Are You Really Solving?

Introduction
I recently reviewed an enterprise intranet portal that had been running on Liferay since version 6.0. The company had upgraded it multiple times over the years -6.0 to 6.4, then to 7.0, and eventually to 7.4 Update 60.
On paper, they were on a modern version of Liferay. In reality, the portal was built like it was still 2010.
If you are a CTO or digital leader at a manufacturing or enterprise company managing a Liferay portal that has been around for 10+ years, this story will feel familiar.
What They Built in 6.0
The original portal was built using the standards of its time:
- Spring MVC Portlets for all UI and business logic
- Spring + Hibernate for data persistence - no Service Builder
- Hook plugins for everything else: JSP overrides, MVC action overrides, auto-login customization, service wrappers
- Plugin SDK, packaged as WAR files
This was perfectly reasonable in 2010. Liferay 6.0 was a Java EE world. Spring MVC portlets were a legitimate pattern. The Plugins SDK was the only official way to build on Liferay. Nobody was wrong for building this way.
The 6.x to 7.0 Upgrade: The First Missed Turn
When Liferay 7.0 arrived, it introduced something significant: OSGI Modularity. The platform moved from a monolithic WAR to a module-based architecture with a proper dependency framework, bnd.bnd manifests, and Liferay Workspace.
This was not just a technical change. It was Liferay telling the ecosystem, "The way we build things is changing."
What did this team do? They moved their code into Liferay Workspace but kept building WARs. No bnd files. No OSGi modules. They fixed compilation errors, updated a few class references, and made the existing code work on 7.0. Technically successful. Architecturally, nothing changed.
This is the pattern I call a version upgrade without a paradigm upgrade. The platform evolved. The implementation stayed in 2010.
The 7.4 Upgrade: Same Pattern, Bigger Problem
By 7.4, Liferay had introduced Liferay Objects, Client Extensions, and a fully headless API layer. The platform was now actively guiding developers away from custom portlet development and toward configuration-driven data modeling and decoupled frontend extensions.
The team did what they had always done: fixed the files that broke, updated dependencies, and made it compile. They are now running 7.4 Update 60 with Bootstrap 3, a legacy jQuery version, Spring MVC portlets, and hooks -all untouched.
And now they are facing a wall.
Why the Next Upgrade Is Different
Liferay's 2026.Q1 LTS release has completed the move from Java EE (javax.*) to Jakarta EE (jakarta.*). This is not a class rename; you can script your way around. The Spring MVC Portlet framework - the one this portal was built on - does not support Jakarta. It simply will not work.
To move to 2026.Q1, they could technically migrate their Spring MVC Portlets to Jakarta using PortletMVC4Spring -Liferay's own fork of Spring Portlet MVC that supports Spring Framework 6.x and Jakarta EE. The namespace references change from javax.portlet.* to jakarta.portlet.* and the portlets continue to work.
But here is the harder truth: Liferay has officially deprecated PortletMVC4Spring in its own documentation. Their guidance is clear - use Client Extensions or Liferay Objects instead. So migrating to PortletMVC4Spring just to pass the Jakarta hurdle means investing real engineering effort to land on a framework. Liferay itself is a dead end. You would be doing the wrong thing correctly.
The technical debt that was manageable in every prior upgrade is now compounding at the worst possible time.
What the Right Path Actually Looks Like
This is not about the next upgrade. It is about making the portal maintainable for the next ten years.
The architecture that makes sense today looks like this:
Liferay Objects for custom data models - structured, workflow-enabled, with auto-generated REST APIs, and no custom Java persistence code required.Objects for custom data models - structured, workflow-enabled, with auto-generated REST APIs, and no custom Java persistence code required.
Client extensions for all UI customization - decoupled from the Liferay container, built in any frontend technology, and unaffected by platform upgrades.
Spring Boot microservices for complex business logic that cannot live inside Liferay - connected via headless APIs, independently deployable, and testable.
This model means that when Liferay releases its next version, your upgrade surface is tiny. Core Liferay upgrades. Your client extensions and microservices stay exactly as they are. No hook migration. No portlet conversion. No emergency compatibility work.
That is the difference between a portal you upgrade and a portal you maintain.
So, Upgrade or Modernize?
Here is how I think about it: if your team can complete the next Liferay upgrade in a few weeks and the implementation approach has genuinely kept pace with the platform, it is an upgrade. Go ahead.
But if every upgrade takes longer than the last, if the team dreads the next version jump, if the words "Spring MVC portlet" and "Jakarta" in the same sentence cause real anxiety - that is not an upgrade problem. That is an architecture problem. And solving an architecture problem with an upgrade project just defers the cost.
The 2026.Q1 Jakarta migration is the right moment to make this call. Not because it forces your hand - though in some cases it does - but because Liferay's current architecture finally gives you a path where future upgrades become genuinely low-risk. Client extensions do not break between versions. Liferay objects do not need porting. The modernization investment you make today pays dividends on every release that follows.
Do the upgrade if you must. But do the modernization if you want to stop having this conversation every two years.
A Thought Before You Decide
Most teams already know they need modernization.
The real hesitation is not what to do - it is how to do it without breaking what is already working.
- How do you transition Spring MVC and Liferay MVC (OSGi) portlets - without rewriting everything - to a future-ready architecture?
- How do you modernize without downtime?
- How do you ensure business continuity while changing the foundation?
Because in reality, no enterprise portal gets a blank slate rewrite.
What we have seen work is a phased modernization approach - starting with the areas causing the highest performance and maintenance pain, then gradually evolving both Spring-based and OSGi-based implementations toward a cleaner, decoupled architecture.modernization approach - starting with the areas causing the highest performance and maintenance pain, then gradually evolving both Spring-based and OSGi-based implementations toward a cleaner, decoupled architecture.
And when done right, this transition can happen without downtime, without disruption, and without forcing a full rewrite.
But that is a deeper conversation.