Welcome to Tech Mastery, your expert source for insights into technology and digital strategy. Explore topics like Adobe Experience Manager, AWS, Azure, generative AI, and advanced marketing strategies. Delve into MACH architecture, Jamstack, modern software practices, DevOps, and SEO. Our blog is ideal for tech professionals and enthusiasts eager to stay ahead in digital innovations, from Content Management to Digital Asset Management and beyond.
Trunk Based Development is a branching model in which developers create short-lived feature branches and merge back into the “trunk” branch, often called as the master branch.
The guiding principals of Trunk Based Development
There is one “trunk” branch where developers merge their changes.
Developers should merge small changes as often as they can.
Merges must be reviewed, tested, and must not destroy the “trunk”.
All code in “trunk” must be release ready at all times.
Feature branches must be short-lived.
Keep your commit messages as concise as possible
Comparing Trunk Based Development to GitFlow
The Trunk Based Branching Model
The below model can be used for scaled teams, the development is done with short-lived feature branches, the changes are often merged to the “trunk”. For small teams, the developers can directly merge the changes to the “trunk” in small chunks.
Changes made in the release branches — snapshots of the code when it’s ready to be released — are usually merged back to trunk as soon as possible. One key benefit of the trunk-based approach is that it reduces the complexity of merging events and keeps code current by having fewer development lines and by doing small and frequent merges.
The developers should experienced enough to make this model successful, this model often creates conflicts if the changes are not reviewed and tested rigorously. Use this model if you are looking to push out a new product fast and want to iterate quickly.
Feature Development with Feature Flags
Trunk Based Development uses Feature Flags as a mechanism to manage new feature releases. A feature flag is simply a boolean condition that modifies the behavior of a component, module, or function in your application.
Following a Feature Flag pattern trades the simplicity of isolated branch workflows, such as GitFlow, in favor of flexible feature rollouts, continuous delivery, and application personalization.
Setting Up Feature Flags
A simple way to begin using feature flags is to maintain a single file containing your feature flags. Let us see how to manage the flags in Typescript with React application through a simple approach. The feature flags can also be managed through external tools like optimizel or launchdarkly
featureFlags.tsconst featureFlags = { hellowordnewfeature: false } export function getFeatureFlag(key){ return featureFlags[key] || false; }helloword.ts //return feature based on the feature flagimport { getFeatureFlag } from "./featureFlags"; const createHelloWord = () => { if(getFeatureFlag("hellowordnewfeature")){ return createNewHelloWord() } return createOldHelloWorld() }
Here the new feature is returned based on the flag “hellowordnewfeature”, if the flag is “true” then the new feature(createNewHelloWord)is returned else the old feature(createOldHelloWorld).
This TypeScript module(featureFlags.ts) can be extended to fetch the features from external or internal services.
Existing Feature Development with Feature Flags
Existing feature development with feature flags is slightly more complex but offers more flexibility for continuous delivery and personalization.
Small Incremental Change
If the proposed feature is a small incremental change, we can modify an existing code path to augment behavior. Take for example adding a new calculation for the total.
featureFlags.tsconst featureFlags = { hellowordnewfeature: false, useNewcalculateTotal:true } export function getFeatureFlag(key){ return featureFlags[key] || false; }// before const calculateTotal = (qty, val) => { return qty * val } // after const calculateTotal= (qty, val, tax) => { if(getFeatureFlag("useNewcalculateTotal")){ return qty * val * tax } return qty *val }
Large Modification
If the proposed feature is large, for example, we want to display a completely new TaxCalculator component, we would need to define a new code path and entry-point for that component.
New feature development with feature flags is simpler than existing features. Since there are no existing code paths for your code to execute, this code path will be disabled by default while this feature is WIP.
The new feature development process with flags should look like this;
create a feature flag for your new feature
begin working on your code
ensure your flag is false before merging into master
merge your code frequently
when the feature is ready for release, remove the flag
Conclusion
Trunk based Development and Feature Flags together can be used for continuous delivery, delivering the features faster to market. Planning them carefully will allow you to quickly deliver the new business features to the system. The feature flags can also be managed through external tools like optimizel or launchdarkly, tools provide SDK to manage the features external to the applications.
This tutorial explains the complete details on configuring sling mapping for resource resolution in Adobe Experience Manager.
Resource mapping is used to define the content mapping and redirects in AEM.
I have enabled two sample domains www.example1.com and www.example2.com pointing to /content/wknd/us/en and /content/wknd/ca/en nodes respectively(sample wknd website nodes).
The flow is as below
The user request for www.example1.com and www.example2.com, the Apache redirects the user to the domain specific landing pages and also redirect the user to the shortened URL(hide/content/wknd/<country code> if user access the page with full content path). The dispatcher pass through the shortened content URL’s to publisher by appending the full content path /content/wknd/<country code>
As first step let us enable the virtual host configurations for www.example1.com and www.example2.com in Apache(Dispatcher)
<VirtualHost *:80>
ServerAdmin [email protected]
DocumentRoot "C:\opt\communique\dispatcher\cache"
ServerName www.example1.com
RewriteEngine On
RewriteRule ^/$ /en.html [R=301]
RewriteRule ^/content/wknd/us/(.*)$ /$1 [NE,L,R=301]
RewriteCond %{REQUEST_URI} !^/apps
RewriteCond %{REQUEST_URI} !^/content
RewriteCond %{REQUEST_URI} !^/etc
RewriteCond %{REQUEST_URI} !^/home
RewriteCond %{REQUEST_URI} !^/libs
RewriteCond %{REQUEST_URI} !^/bin
RewriteCond %{REQUEST_URI} !^/tmp
RewriteCond %{REQUEST_URI} !^/var
RewriteRule ^/(.+)$ /content/wknd/us/$1 [NC,PT,L] <Directory />
<IfModule disp_apache2.c>
SetHandler dispatcher-handler
</IfModule>
Options Indexes FollowSymLinks Includes
# Set includes to process .html files
AddOutputFilter INCLUDES .html
AddOutputFilterByType INCLUDES text/html
AllowOverride None
</Directory>
</VirtualHost><VirtualHost *:80>
ServerAdmin [email protected]
DocumentRoot "C:\opt\communique\dispatcher\cache"
ServerName www.example2.com
RewriteEngine On
RewriteRule ^/$ /en.html [R=301]
RewriteRule ^/content/wknd/ca/(.*)$ /$1 [NE,L,R=301]
RewriteCond %{REQUEST_URI} !^/apps
RewriteCond %{REQUEST_URI} !^/content
RewriteCond %{REQUEST_URI} !^/etc
RewriteCond %{REQUEST_URI} !^/home
RewriteCond %{REQUEST_URI} !^/libs
RewriteCond %{REQUEST_URI} !^/bin
RewriteCond %{REQUEST_URI} !^/tmp
RewriteCond %{REQUEST_URI} !^/var
RewriteRule ^/(.+)$ /content/wknd/ca/$1 [NC,PT,L]
<Directory />
<IfModule disp_apache2.c>
SetHandler dispatcher-handler
</IfModule>
Options Indexes FollowSymLinks Includes
# Set includes to process .html files
AddOutputFilter INCLUDES .html
AddOutputFilterByType INCLUDES text/html
AllowOverride None
</Directory>
</VirtualHost>
“DispatcherUseProcessedURL on” configuration should be enabled in dispatcher configuration to enable the Pass Through URL’s to work
e.g
<IfModule disp_apache2.c># location of the configuration file. eg: 'conf/dispatcher.any'
DispatcherConfig conf/author_dispatcher.any
# location of the dispatcher log file. eg: 'logs/dispatcher.log'
DispatcherLog logs/dispatcher.log
# log level for the dispatcher log, can be either specified
# as a string or an integer (in parentheses)
# error(0): Errors
# warn(1): Warnings
# info(2): Infos
# debug(3): Debug
# trace(4): Trace
DispatcherLogLevel Debug
# if turned on, the dispatcher looks like a normal module
DispatcherNoServerHeader Off
# if turned on, request to / are not handled by the dispatcher
# use the mod_alias then for the correct mapping
DispatcherDeclineRoot Off
# if turned on, the dispatcher uses the URL already processed
# by handlers preceeding the dispatcher (i.e. mod_rewrite)
# instead of the original one passed to the web server.
DispatcherUseProcessedURL on
# if turned to 1, the dispatcher does not spool an error
# response to the client (where the status code is greater
# or equal than 400), but passes the status code to
# Apache, which e.g. allows an ErrorDocument directive
# to process such a status code.
#
# Additionally, one can specify the status code ranges that should
# be left to web server to handle, e.g.
#
# DispatcherPassError 400-404,501
DispatcherPassError 0#
# DispatcherKeepAliveTimeout specifies the number of seconds a
# connection to a backend should be kept alive. If not set or
# set to zero, connections are not kept alive.
#
#DispatcherKeepAliveTimeout 60</IfModule>
Now both the domains www.example1.com and www.example2.com are accessible, this will display the mapped landing page content but the internal page links still point to the complete URL(/content/wknd/<<country code>>)
Let us now enable the run mode specific(my case the server is enabled with additional run mode “dev”) ResourceResolver configuration — “Apache Sling Resource Resolver Factory” to reverse map the internal full content path to the shortened URL— enable the mapping for all the required content paths.
Now the internal page links are transformed to shortened URL based on the URL mappings in the Resource Resolver.
This approach has multiple draw backs —
the mapping don’t supports the regex expressions
it doesn’t support cross-site links because this rewriting method is not domain-aware
reverse map the content path to the corresponding domain wont work
The incoming URL will be resolved without any issue as the dispatcher always send the complete path(e.g /content/wknd/us/en.html) to the publisher — http://localhost:4503/system/console/jcrresolver.
The content path is not reversed mapped to the domain URL
This will create issues while supporting multiple domains in the AEM platform and need to link the pages between sites(the components should identify the domain corresponding to the content path while linking the pages from different sites — ResourceResolver.map(…))
We should use the etc mapping to enable the forward resolving(resolve the incoming URL to content path) and reverse mapping (map the content path to the domain)
As a first step create an environment specific mapping folder (sling:Folder)— /etc/map.dev.publish, the etc mapping configurations won’t honor the run mode but it is possible to enable the environment specific configuration by enabling different folders e.g map.dev.publish, map.uat.publish, map.stage.publish and map.prod.publish and configure the folder to the run mode specific JCR Resolver configuration( “Apache Sling Resource Resolver Factory”).
Create a folder http or https(sling:Folder) under map.dev.publish based on the protocol used to communicate to publisher, use https even the SSL is terminated at load balancer level — the mapping details can be stored to code repository for better management.
Enable the below Sling Mappings under /etc/map.dev.publish /http(s) based on the protocol used— this will enable the forward mapping(resolve) — map the incoming URL to the content path(forward mapping is optional as the dispatcher is already sending the complete content path to publisher) and reverse mapping (map)— map the content path to shortened DNS URL.
Now the forward and reverse mapping works as expected
The resolve and map methods of org.apache.sling.api.resource.ResourceResolver interface can be used to resolve/map the resources in java services/components.
Let us now enable some additional sling mappings
Resolve(Forward Mapping) the specific incoming URL to a different content path.
e.g the incoming request to the sitemap.xml file should be internally mapped to a different location where the file is located(under /content/wknd)
Create a node sitemap_mapping of type “sling:Mapping” under /etc/map.dev.publish/www.example1.com and add the below properties
Now the request to sitemap.xml — http://www.example.com/sitemap.xml displays the content from /content/wknd/sitemap.xml(enabled sample sitemap.xml file under /content/wknd)
Multiple forward mappings can be created as required for the specific domain.
Reverse Map the URLs to remove the .html extension from the page links
Let us now enable additional reverse mapping to remove the html extension from internal page links.
Create a node reverse_no_html of type “sling:Mapping” under /etc/map.dev.publish/www.example1.com and add the below properties
The sling mapping helps us to map the incoming request to the internal content path and at the same time map the internal content path to the complete DNS based shortened URL. This will enable the AEM platform to support multi tenants and allows the author to cross link the websites just through the content path(AEM automatically maps the content path to the domain URL)