A few months ago, AEM became publicly available as a Cloud Service. As a consequence, the future of the Gradle AEM Plugin (GAP) - a tool that drastically improves developer efficiency in AEM application development - became a little uncertain. This is because Cloud Manager, the flagship automation tool of AEM as a Cloud Service, does not support the Gradle build system. Only Maven is supported. We understand this choice, but a lot of people who actually have a chance to work with Gradle & GAP value those tools a lot. Build speed is n‑times faster, configuration capabilities are much richer due to the dynamic nature of the build script concept used in Gradle, in contrast to static Maven pom.xml files. The additional tools around AEM application development improve the developer's daily routine by automation. For example, content synchronization from and AEM instance takes seconds, not minutes, and downloaded JCR content is even normalized on-the-fly. All of the improvements designed especially for A Better AEM Developer Experience, which was the topic of our talk at the adaptTo() 2018 Conference :)

Whoever does not try will not find out.

That's what I'm often saying to myself and my colleagues when we are trying something completely new. It's hard to come up with new things. It's even more difficult to defend ideas. As an example of such an unusual idea could be an approach for addressing the question - how to improve the developer's experience when they are using AEM Cloud Manager enforcing usage of Maven build tool? As proposing just switching build system from Maven to Gradle is not an option, how about using them both? At first, it could look a bit controversial, but… after deeper investigation and tests, that combination has strong benefits.

The first approach identified is the dual-build. It is about mixing the two build systems, adding Gradle build files to existing AEM projects while keeping them independent from existing Maven build files.

The second, more unusual approach is about splitting Maven build execution into steps - executing Maven for one module at a time, one after another. Let's call it the hybrid-build.

Comparison

Going further, let's compare new approaches with existing one based on own extended fork of Adobe AEM Archetype which allows to generate dual-build or hybrid-build projects interactively:

Task Build trait Maven build Dual-build Hybrid-build
Build and deploy packages Command mvn clean install ‑PautoInstallSinglePackage ‑PautoInstallSinglePackagePublish sh gradlew :all:packageDeploy sh gradlew :all:packageDeploy
First time 1m 11s 1m 5s (1m 29s) 1m 44s* (2m 52s)
Next time 43-45s 1-3s 1-3s
JS/CSS change 38-39s 9s (15-16s) 24s (31-35s)
Java change 33-36s 6s (14-15s) 25s (30-32s)
Content change 30-36s 3s (11-12s) 11s (16-18s)
Setup AEM environment Command N/A sh gradlew setup sh gradlew setup
Time 30-90m 5-7m 5-7m
Clean AEM dispatcher cache Command N/A sh gradlew environmentReload sh gradlew environmentReload
Time 1m 3s 3s
All-in-one Command N/A sh gradlew develop sh gradlew develop
Time 30-90m 6-8m 7-9m

Assumptions

  • Measurements done on MacBook Pro 2019 (Core i5, 16 GB RAM),
  • Using AEM as a Cloud Service SDK (version 2020.6.3800),
  • Building and deploying packages:

    • using all assembly package,
    • to both AEM author and publish instances,
  • Setting up AEM environment includes:

    • downloading, configuring, turning on (with debug port opened) then awaiting stable AEM author and publish instance,
    • deploying one extra dependent CRX package (Felix Search Web Console Plugin),
    • configuring replication agents for fully operational pages activation and deactivation,
    • downloading, configuring, turning on AEM dispatcher using Docker,
  • Time value 30-90m proposed for setting up manually AEM environment is just estimation, effectively time depends on AEM developer experience,
  • Cleaning AEM dispatcher cache includes also restarting Apache HTTPD Server,
  • The All-in-one task encapsulates all tasks mentioned above - it sets up a complete local AEM environment from scratch.

Package deployments

Generally, dual and hybrid builds outperform pure Maven when it comes to execution time (with one exception explained later). This is especially true when helpful but time-consuming extra features of GAP (values in parentheses in the table) are disabled:

However, the best build performance is definitely the most important advantage of the dual-build approach. The hybrid-build approach also improves it, because of parallel Maven execution.

What's interesting about hybrid-builds is that CRX packages built by Maven are deployed by GAP. This leverages GAP's advanced features, designed for rock-solid AEM deployments, and helps a lot when some or all AEM instances are not stable for unknown reasons. GAP can detect and report precisely what is wrong with any of them. It is also worth mentioning that GAP deploys CRX packages to all instances in parallel, further saving time.

Not only the build times matter. When using Maven, people tend to execute the same command repeatedly, aiming to ensure that all packages and application changes are propagated to AEM instances correctly. However, Maven is not capable of caching the unchanged parts of an application. It is unable to:

  • avoid running Node/Webpack when there are no changes in ui.frontend module,
  • avoid compiling unchanged Java code in core module,
  • and so on…

But Gradle does it all. The problem related with incremental builds is interesting also from even more generic perspective. In AEM projects, there are usually separate roles:

  • AEM Frontend developer who is changing most often module ui.frontend,
  • AEM Backend developer who is changing modules core, ui.apps etc

They most often perform changes in a single module and would like to build only that module (don't want to build other modules). In that case, AEM developer needs to know alternative build commands when using Maven (cd ui.apps && mvn clean install -PautoInstallPackage,autoInstallPackagePublish). Changing a current working directory and remembering these profiles with long names might be discouraging.

This well-known scenario plays much better with Gradle-powered AEM Maven builds. AEM Developer can use all the time the same command - just sh gradlew. Then Gradle detects changed parts of an application and rebuilds related modules only. This really makes a big difference! The developer works more effectively because the shortest build time and application consistency are guaranteed. There is no option to forget about rebuilding some modules - when trying to optimize build time by building only changed modules by hand when using Maven. That's very comfortable and is protecting developer against errors.

The little disadvantage related to hybrid-build seems to be only the first build time. As of Maven execution is split to parts, there could be a little overhead/time penalty here because of a need for running Maven process multiple times. However, effectively it might be faster because Gradle may execute Maven processes in parallel (especially when AEM project will contain more modules).

Automated local environment setup

Jokingly speaking, GAP fills the gap :) It offers:

  • Automated AEM environment setup:

    • Adobe provides only guide describing the manual steps to be done.
    • No official way for setting AEM environment in an automated manner.
  • Perfectly-integrated tools useful for AEM application development:

Recommendations

When to use each build approach?

  • Maven build:

    • For deployments to higher environments e.g using Cloud Manager to ensure the best compatibility,
  • Dual-build:

    • Having best build performance and developer experience is most valuable,
    • Maintaining both build systems, effectively updating files build.gradle.kts and pom.xml is affordable,
  • Hybrid-build:

    • It is valuable to improve build performance of existing AEM Maven build system,
    • Maintaining two separate build systems as in dual-build approach may cost too much,
    • Risk of having differences in built CRX packages by separate build systems because of misconfiguration is too high (when comparing to dual-build).

Summary

As the comparison shows, using Gradle/GAP could improve all AEM Maven builds. Mixing build systems may require additional skills, such as learning Gradle a little bit. However, if we consider:

  • the time being wasted between incremental application changes done by AEM developers,
  • the potential in allowing the team to focus on making software changes by providing them with immediate feedback,

then introducing a separate build system in existing AEM projects starts to sound less like a brave and crazy, bleeding-edge combination and more like a sensible move, translating to faster project delivery, happier content authors and better AEM sites.

To try and see how the dual or hybrid build works in practice, check out the ready-to-use AEM archetypes available on GitHub:

https://github.com/Cognifide/aem-project-archetype

P.S. If you find this topic interesting, make sure to see our live demo at adaptTo() 2020.