The Truth about AEM Run Modes

The Adobe Experience Manager as a Cloud Service (AEM) is a very flexible platform. It allows (customer) developers to extend the two main services, author and publish via Java code. In many cases the exact same code needs to run on both services. However, there are use cases where the code should only run on one of them or behave differently. Similar, a different behaviour or configuration should be used in a development environment versus the production environment.

AEM provides the concept of run modes. A run mode indicates the type of the service as well as the environment type the service is running in. Therefore mutual exclusive run modes exists for author and publish as well as for dev, stage, and prod.

It is important to note that the concept of run modes is not a runtime concept. Following the twelve-factory methodology it has been designed as a deployment and provisioning concept. However, over time some bad pracices spread using run mode information at runtime to select different configurations or code paths.

Don’t Deploy Unused Code

If you want to run different code on author and publish, make the distinction through different deployments: deploy different code on author than on publish. This can be easily achieved by putting common code into a shared bundle and creating separate bundles for author or publish. Use a similar approach for components residing in the repository. This way you do not deploy unused or dead code.

It is bad practice to deploy the exact same code and then clutter the code with if statements checking for the run mode. The clean way is to just deploy what needs to run on a service.

Use Environment-Specific Configuration Values

It is a common use case to have different configuration values for different environments. For example when connecting to external services, different endpoints might be used for development than for production. For this create an OSGi configuration that uses placeholders:

{
  "service.endpoint": "$[env:EXTERNAL_SERVICE_URL]",
  "service.user":"$[env:EXTERNAL_SERVICE_USERNAME]",
  "service.password":"$[secret:EXTERNAL_SERVICE_PASSWORD]"
}

Use Service-Specific Configurations

Similar to environment-specific configuration values there are use cases where you want to have a different configuration for author than for publish. For this just create two OSGi configurations: one is bound to the author run mode, the other one is bound to the publish run mode.

This can also be combined with placeholders for having environment-specific values which also differ between author and publish.

Advanced Use Cases

Following the above three rules gives a clear separation and code. It avoids special casing within code which is over time hard to understand and maintain. Even more advanced use cases can be handled this way. For example if you want to have the same service on author and publish but with a different algorithm: Author is always using fresh data within a service, while the data is cached on publish within that service. In such case you might want to use a behavioural pattern like the strategy pattern. This goes back to the first rule. Create different code deployments for author and publish and then use the code through OSGi services from some common shared code base.

Use Run Modes for Deployment and Provisioning

In summary, it is good practice to leverage run modes for describing the deployment and provisioning. Define precisely which bundles and configurations apply to both, author and publish, as well as which of them only apply for one service.

Leverage environment-specific configuration values to provision different values for each environment type. But refrain from trying to deploy different code per environment type.