Key Mistakes You Should Avoid When Developing in Liferay
In the current Liferay environment, a ‘good’ Liferay Engineer is very much different from what they used to be a few years back. The worst error could be to trust in building monolithic Java modules having a large size and very high complexity, producing a huge technical debt, and not adopting a ‘Platform First’ approach that stays decoupled with Client Extensions and Low Code capabilities.
In Liferay, we say, develop without reinventing the wheel, and use the wheel for further innovations
Here is the complete list of mistakes you should avoid and the replacements you can use for future compatibility :
1. Backend : Stop Coding, Start Configuring
The worst thing that happens in the backend is when a solution or workaround in Java code is implemented when the same solution already exists in the platform.
Avoid : Service Builder for Custom Data Models
The Mistake : Creating service.xml, code, and SQL tables for each new business entity manually.
Solution : Liferay Objects
Use the OOTB Objects Engine to build the data structures.
Gain automatic creation of Headless APIs, UI views, and security integration, all with no Java coding!
Easy to manage entity relationships using the object model builder view.
Can be used with form containers to create no-code/low-code forms.
Avoid : Job Schedulers
The Mistake : Using Liferay’s job scheduler inside the OSGi container.
Solution : Client Extensions
Move the background processing logic into a Spring Boot application.
Use Spring / Node Cron Jobs to make calls via Headless REST APIs.
In this way, you make sure that a heavy background process doesn’t bring down the JVM of your portal.
Avoid : Manual Data Import Scripts
The Mistake : Creating custom Groovy scripts or “Data Migrators” written in Java to initialize site data.
Solution : Batch Client Extensions
Use the Batch Client Extensions where the data is added in JSON format.
The batch engine in Liferay handles all the work when exporting and importing entities such as Objects or Web Contents.
A single deployment can set up the whole site, avoiding errors that developers used to face during the import/export.
Avoid : Model Listeners and Groovy Scripts in Workflow
The Mistake : The usage of complex Kaleo XML or Java Model Listeners to drive logic changes in data.
Solution : Object Actions and Provided Workflow Task Assignments and Actions.
We can set actions from the ‘Actions’ tab available in the Liferay Objects.
The result will be the execution of Webhooks that can invoke the external Microservices or Client Extensions to perform the logic.
Groovy scripts in the Kaleo task assignments are harder to maintain and may break if Liferay’s version is upgraded.
2. Frontend : From Monoliths to Micro-Frontends
The frontend development in Liferay has shifted from a ‘Theme’ mentality to a ‘Modular’ mentality.
Avoid : MVC Portlets and JSPs
The Mistake : Creating JSR-286 Portlets utilizing Server-side Rendering and old Taglibs.
Solution : Page Fragments & Custom Elements.
Use Fragments (HTML/CSS/JS) for content-rich UI. For complex apps, use Custom Element Client Extensions.
This allows the app to be built purely with React or Vue, to run as its own application off of its own server, with the app then displayed within the Liferay page as a web component.
Avoid : WAR Themes and AlloyUI
The Mistake : Packaging the entire look and feel of the site in a single WAR file and using the deprecated Liferay JavaScript libraries.
Solution : Style Books & Theme CSS Client Extensions.
Style Books can be utilized to control branding, such as colors and fonts, through UI.
Alternatively, a Theme CSS Client Extension can be used for customized CSS, which will work regardless of version.
Avoid : Monolithic Themes for Global Assets
The Mistake : Including Favicons and JavaScript in a Weighty WAR file.
Solution : JS and Favicon Client Extensions.
JS Client Extensions : Rather than putting the script tag injection inside the theme’s *portal_normal.ftl*, use the JS Client Extension. This way, you get the ability to inject analytics or global libraries across the site without having to redeploy the theme.
Favicon Client Extensions : You can use the new dedicated extension to deliver site-wide favicons, making it simple to change branding without having to write code.
3. Content Delivery : Beyond the Asset Publisher
Heavy customization of Asset Publisher to provide functionalities it was not intended to, is one of the mistakes that creates unmanageable code with FreeMarker.
Avoid : Heavy Customization of Asset Publisher
The Mistake : Using the Asset Publisher for all lists and writing the complex ADTs (Application Display Templates).
Solution : Collections & Search Blueprints.
Collections : Utilize the “Collections” to create dynamic groups of data.
Collection Display Fragment : Here, the data is mapped to the user interface.
Search Blueprints : Utilize the visual query builder tool to design complex filters that target personalized content that transcends the typical functionality of the Asset Publisher search.
Summary Checklist : Legacy vs. Modern
Category | Legacy Mistake (Avoid) | Modern Standard (Use This) | Why? |
Data Modeling | Service Builder (service.xml) | Liferay Objects | Creates tables with no code that are integrated with Liferay’s core frameworks. |
Data Migration | Groovy Scripts / Custom Java | Batch Client Extensions | Bulk standard processing can be facilitated using the Batch Engine. |
Custom Apps (UI) | MVC Portlet / JSPs | Custom Element CX (React/Vue) | The frontend is entirely decoupled and runs as a Web Component, ensuring it is safe and stable in upgrades. |
Static Content (UI) | Web Content Display / ADTs | Page Fragments | Drag-and-drop tools enable the creation of experiences with field mapping. |
Schedulers | OSGI JobScheduler | Microservice CX (Spring Boot / Node) | The critical or resource-heavy processing part is delegated to external containers through cron jobs. |
Event Triggers | Model Listeners / Wrappers | Object Actions | Triggers the external API when there are changes in the data. |
Querying | Custom SQL / Dynamic Query | Search Blueprints / Collections | Visual query builders make it possible to construct queries for Elasticsearch without writing Java or SQL code. |
Display Listings | Asset Publisher | Collection Display Fragments | Pages will load faster, and designing will be easier because FreeMarker(ADTs) will be removed. |
Global CSS (Design) | WAR Themes (CSS files) | Theme CSS Client Extensions | CSS can be injected dynamically without the need for restarts in the portal or any versioning constraints. |
Branding (Design) | Theme hard-coding | Style Books | The colors and fonts can be immediately updated using the UI via CSS variables. |
Assets (Global JS) | portal_normal.ft/scripts | JS Client Extensions | Analytics services, for example, “Google Tag Manager,” or code libraries, can also be introduced at a site level without creating a theme. |
Assets (Site Icons) | Theme Images / Favicons | Favicon Client Extensions | The icons can be replaced by marketing teams using the interface offered by CX. |
Workflows | Kaleo XML / Java Actions | Workflow with Webhooks | Workflows can be linked to the external Node or Python logic through APIs. |
Custom Forms | Dynamic Data Lists (DDL) | Liferay Forms + Objects | A contemporary form builder is able to connect the fields directly to the Object entries for efficient data management. |
Setup (Provisioning) | Resource Importer | Site Initializer CX | Package entire sites (pages, assets, config) as a deployment unit. |
Conclusion
Any time before performing a manual module/component creation operation, always pause and ask yourself: ‘Can I do this with an Object, a Fragment, or Client Extensions?’ If yes, then you will save yourself from a whole lot of upgrade challenges in the future.


