A clean, elegant code base goes a long way. It helps developers read and modify the code, sets a good foundation for future development, reduces technical debts and maintenance overhead, makes code easier to unit test, follows best practices, and much more. This still holds true in the Adobe Experience Manager (AEM) world. However, many projects that I have been on, or done health assessment for, have some degree of messy or unnecessary codes.
In this post, I categorize AEM development best practices into six sections and outline how you can write clean codes.
Note: a. These lists will be updated as I find more suitable item, and you are also welcome to comment if you have more.
b. Even though these lists are specific for AEM technologies, many of the practices are agnostic to technology.
Project:
- Generate project code base from AEM project archetype.
- Remove all unused modules, sample components, structures, classes etc.
- Clean up unused maven dependencies in POM file.
- For a multi-tenant project, keep shared functionality in mind. Don’t exploit the project repositories.
- Define consistent naming convention throughout the project.
node names should always be all lower case and words should be separated using hyphens. Property names should always be camel case, starting with a lower case letter. And components (JSP/HTML) should all be in lower case with words again separated using hyphens. You can find maven code style and code conventions here. - Keep code build time short.
- Avoid implementing YAGNI (or You Aren’t Gonna Need It) features. If you aren’t going to need it, don’t implement it! Keep it current, keep it simple.
- Leverage the tools Adobe or ACS commons that are provided. This will help you achieve more with less.
Templates/ Components:
- Keep the number of templates small. The number of templates should be equal to the number of fundamentally different page structures on the website.
- Develop a base page template with granular files/sections, so other templates can overlay specific file/section, but inherit common ones from a base.
- Consider using an editable template for sharing responsibilities with template super-user.
- Use Sling Resource Merger to inherit and overlay components.
- Design components with functionality and re-usability in mind, and avoid creating new components with similar functionalities.
- Consider extending components from the base for extended functionalities. This is helpful for designing components for multi-tenant projects where you have similar components with little variation.
- Add model or view logic only where it’s needed.
- Leverage the production-ready components from AEM WCM core components.
Clientlibs:
- Clientlib-all should be included once, ideally on the page level. Each clientlibs call is a server call, and you may expose your application structure.
- Use component/module clientlib for on-demand or shared purposes.
- Consider versioned clientlibs for a cache friendly clientlibs solution.
- Clientlibs should be minified and gzipped for better performance.
- Have a dedicated structure in your project for authoring clientlibs. I recommend considering OOTB structure.
HTML Template Language (HTL)/ Sightly:
- Start writing HTL now. It’s much preferred over JSP.
- <sly> tag automatically unwraps for you, so you do not need data-sly-unwrap anymore.
- Check component markup in publish and make sure that no extra or unnecessary HTML has been generated.
- Use the sightly template if section should be reused or overlayed by other components.
- Use sightly context wisely, some elements have default context set.
- Write sightly comment if possible. HTML comment gets rendered in final markup.
<!--/* This is a sightly comment */--> <!-- Avoid using HTML comment -->
- Put data-sly-test in a variable if it is reusable within the same file. The variable has global scope within HTL file. For example:
<p data-sly-test.abc="${a || b || c}">is true</p> <p data-sly-test="${!abc}">or not</p>
Touch UI Dialog:
- Have a consistent dialog structure. In CoralUI 3 dialog, it follows this structural pattern: dialog – container – tabs – container- fixedcolumns- container – individual fields. Remember to clean up unnecessary structure nodes or properties.
- Dialog tabs or fieldsets can be shared and reused.
- Use Sling Resource Merger to inherit and overlay touch UI dialog fields. For example, if you have an extended component (with sling:resourceSuperType property), you can use different resource merger properties like sling:hideProperties, sling:hideResource, sling:hideChildren, or sling:orderBefore to construct your component dialog.
- Simply do not copy and paste, develop just what you need.
- In component dialog edit config, you should only develop applicable configurations and in place editors. Clean up or remove any unnecessary ones.
- Leverage Granite UI common attributes, foundation validation, and Adobe/ Granite API whenever possible. This will keep your script more scalable and make it easier to upgrade to new versions of AEM/Granite/CoralUI.
- For RTEs in your project, you can share RTE plugins and configurations for both touch UI dialog and in place editor to ensure consistent authoring experience.
- Leverage dialog conversion tool to convert component dialog.
Java/JUnit:
- Have a consistent formatted structure, proper java docs, and coding guidelines across the company/ team.
- Consider style checker for unified code style. For example, there’s Checkstyle and PMD, both of which can be used in IDE or as Maven plugin. Adobe Consulting Services has established coding standards which you can reference and implement.
- Develop util class for frequently used methods.
- Log properly (info, error, debug levels) and with meaningful descriptions. See the Adobe tips here. For slf4j specifically, use parameterized logging over concatenation.
- Use abstract class to provide common abstract logics.
- Use API constants (JCR, Sling, AEM app specific) whenever possible.
- It’s strongly recommended to bind Sling servlets to resource type rather than path.
- Avoid long running service sessions when writing into Apache Oak. For details, see this blog and Jackrabbit Oak documentation.
- JCR query operations are expensive, so it should be used properly and fine-tuned For rendering requests, I recommend that you consider traversing content tree.
- Consider wcm.io AEM context for Sling Model JUnit test.
- Consider a maven plugin for JUnit test report.
Creating and maintaining a clean, elegant code base is not an easy task It takes a lot of effort over the development lifecycle. But take action now! Keep these best practices in mind when you design and develop solutions, document the norms and conventions your team follow, have a code review process set up and start making changes. Your team will thank you in the long run.
If you would like additional references, check out the examples below.
AEM WCM core components: https://github.com/Adobe-Marketing-Cloud/aem-core-wcm-components
ACS common, tool, sample: https://github.com/Adobe-Consulting-Services/acs-aem-commons
https://github.com/Adobe-Consulting-Services/acs-aem-tools
https://github.com/Adobe-Consulting-Services/acs-aem-samples
We retail: https://github.com/Adobe-Marketing-Cloud/aem-sample-we-retail
My blog repo: https://github.com/guangweiyao/blog.
Very well documented!