<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/migration/</id>
  <link href="https://www.endpointdev.com/blog/tags/migration/"/>
  <link href="https://www.endpointdev.com/blog/tags/migration/" rel="self"/>
  <updated>2025-11-25T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>Migrating from Legacy Networking to systemd-networkd on Ubuntu 24.04</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/11/legacy-networking-to-systemd-networkd/"/>
      <id>https://www.endpointdev.com/blog/2025/11/legacy-networking-to-systemd-networkd/</id>
      <published>2025-11-25T00:00:00+00:00</published>
      <author>
        <name>Bharathi Ponnusamy</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/11/legacy-networking-to-systemd-networkd/banner.webp&#34; alt=&#34;Apartment buildings and a domed white Indian building in Leh city, India sit among trees in front of a layered mountain range, capped with clouds.&#34;&gt;&lt;/p&gt;
&lt;!-- photo by Bharathi Ponnusamy --&gt;
&lt;p&gt;During a recent Ubuntu server upgrade, I migrated a production system from the legacy ifupdown networking stack to the modern systemd-networkd service. This migration was part of preparing several remote production servers for a smooth upgrade to Ubuntu 24.04, which relies heavily on systemd-managed components.&lt;/p&gt;
&lt;p&gt;In this post, I’ll walk through the full migration process, how we automated it safely across multiple remote servers, and the lessons learned from implementing rollback and watchdog mechanisms for zero-downtime transitions.&lt;/p&gt;
&lt;h3 id=&#34;why-migrate-to-systemd-networkd&#34;&gt;Why migrate to systemd-networkd?&lt;/h3&gt;
&lt;p&gt;Ubuntu 24.04 uses systemd-networkd as the default backend for network management. The traditional ifupdown scripts (/etc/network/interfaces) are no longer the recommended way to configure networking. Some of the modern features enabled by systemd-networkd include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Predictable network interface naming (e.g., ens3, eth0, etc.)&lt;/li&gt;
&lt;li&gt;Built-in support for bridges, VLANs, and bonds&lt;/li&gt;
&lt;li&gt;Faster boot times with asynchronous network handling&lt;/li&gt;
&lt;li&gt;Unified configuration under /etc/systemd/network&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Migrating now also avoids future compatibility issues and takes advantage of systemd’s integrated logging and management.&lt;/p&gt;
&lt;h3 id=&#34;step-1-check-the-current-setup&#34;&gt;Step 1: Check the current setup&lt;/h3&gt;
&lt;p&gt;Before the migration, I confirmed the system was still using ifupdown:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ls /etc/network/interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat /etc/network/interfaces&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Output showed legacy configuration similar to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;auto eth0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;iface eth0 inet static
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address 10.42.41.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    netmask 255.255.0.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-2-install-and-enable-systemd-networkd&#34;&gt;Step 2: Install and enable systemd-networkd&lt;/h3&gt;
&lt;p&gt;Since systemd-networkd wasn’t already active, I installed and enabled it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install systemd-networkd systemd-resolved -y
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl enable systemd-resolved&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, I stopped ifupdown to prevent conflicts.&lt;/p&gt;
&lt;h3 id=&#34;step-3-create-a-network-configuration-file&#34;&gt;Step 3: Create a network configuration file&lt;/h3&gt;
&lt;p&gt;Each interface now has its own .network file under &lt;code&gt;/etc/systemd/network/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;DHCP-based interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# /etc/systemd/network/10-eth0.network
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Match]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name=eth0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Network]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DHCP=yes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Static interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# /etc/systemd/network/20-eth1.network
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Match]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Name=eth1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[Network]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Address=10.42.41.1/16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Gateway=10.42.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DNS=8.8.8.8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DNS=1.1.1.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-4-configure-systemd-resolved&#34;&gt;Step 4: Configure systemd-resolved&lt;/h3&gt;
&lt;p&gt;This step ensures your system’s DNS resolver works correctly with systemd-networkd.&lt;/p&gt;
&lt;p&gt;systemd-resolved listens locally on 127.0.0.53 and handles DNS resolution, caching, and per-interface settings.&lt;/p&gt;
&lt;p&gt;Link your system’s &lt;code&gt;/etc/resolv.conf&lt;/code&gt; to use it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then to verify:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;resolvectl status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we skip this, the system might lose DNS resolution after reboot. Applications like apt, curl, and ping may fail to resolve hostnames even though interfaces are up.&lt;/p&gt;
&lt;h3 id=&#34;restart-and-verify&#34;&gt;Restart and verify&lt;/h3&gt;
&lt;p&gt;Restart both services:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl restart systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo systemctl restart systemd-resolved&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Check active links:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;networkctl list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Detailed view for one interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;networkctl status eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sample output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;● 2: eth0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       Type: ether
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      State: routable (configured)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     Address: 10.42.41.1/16
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     Gateway: 10.42.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     DNS: 8.8.8.8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Test:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ping -c 3 8.8.8.8
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ping -c 3 google.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;step-6-reboot-and-confirm&#34;&gt;Step 6: Reboot and confirm&lt;/h3&gt;
&lt;p&gt;Reboot once to ensure everything persists. After the reboot,&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl status systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl status systemd-resolved&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and confirm interface and DNS are still functional.&lt;/p&gt;
&lt;h3 id=&#34;handling-remote-migration-and-downtime-risks&#34;&gt;Handling remote migration and downtime risks&lt;/h3&gt;
&lt;p&gt;Performing this migration remotely on production systems without physical console access required special care. I prepared several layers of protection to prevent accidental lockouts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Console or IPMI verification&lt;/strong&gt;: Ensured each node had out-of-band access in case SSH connectivity was lost.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Timed rollback safety script&lt;/strong&gt;: A background process automatically reverted to legacy networking if the new configuration didn’t come up within a few minutes.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&lt;/span&gt;sleep &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;300&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; ! ping -c1 8.8.8.8 &amp;amp;&amp;gt;/dev/null; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    systemctl disable systemd-networkd
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    systemctl &lt;span style=&#34;color:#038&#34;&gt;enable&lt;/span&gt; networking
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    reboot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;automating-the-migration-and-rollback-process&#34;&gt;Automating the migration and rollback process&lt;/h3&gt;
&lt;p&gt;To streamline upgrades across multiple servers, I created automation scripts that handle migration, rollback, and ongoing monitoring.&lt;/p&gt;
&lt;h4 id=&#34;migrate_to_systemd_networkdsh&#34;&gt;migrate_to_systemd_networkd.sh&lt;/h4&gt;
&lt;p&gt;This script performs a complete, logged migration with built-in validation and rollback.
It checks for valid .network files, disables legacy services, enables systemd-networkd, tests connectivity, and if all retries fail, restores the old configuration automatically.&lt;/p&gt;
&lt;p&gt;Key features&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Validates configuration syntax using systemd-analyze verify&lt;/li&gt;
&lt;li&gt;Masks conflicting services (&lt;code&gt;ifup@eth*&lt;/code&gt;, NetworkManager)&lt;/li&gt;
&lt;li&gt;Performs up to three connectivity tests&lt;/li&gt;
&lt;li&gt;Restarts SSH and dependent services&lt;/li&gt;
&lt;li&gt;Schedules a controlled reboot or user-confirmed one&lt;/li&gt;
&lt;li&gt;Rolls back cleanly if connectivity fails&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;upgrade_watchdogsh&#34;&gt;upgrade_watchdog.sh&lt;/h4&gt;
&lt;p&gt;During do-release-upgrade, networking may restart multiple times.
This watchdog script runs in the background to ensure systemd-networkd remains active and services like SSH come back automatically.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Executes every 10 minutes&lt;/li&gt;
&lt;li&gt;Verifies /etc/systemd/network/*.network exists and services are running as expected if it fails, this script restarts the service&lt;/li&gt;
&lt;li&gt;Restarts systemd-networkd, ssh, and dependent services&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, both scripts allowed us to perform fully remote OS upgrades with zero manual downtime and consistent recovery paths.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Migrating to systemd-networkd modernizes Ubuntu servers and simplifies long-term maintenance.
With the automation scripts and safety mechanisms in place, we successfully migrated multiple remote servers with no downtime and automatic rollback protection.&lt;/p&gt;
&lt;p&gt;This upgrade not only prepares infrastructure for Ubuntu 24.04 but also integrates cleanly with our automation and monitoring systems, ensuring reliable networking for years ahead.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to Migrate from Struts 2 to Struts 6</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/04/migration-from-struts2-to-struts6/"/>
      <id>https://www.endpointdev.com/blog/2025/04/migration-from-struts2-to-struts6/</id>
      <published>2025-04-17T00:00:00+00:00</published>
      <author>
        <name>Kürşat Kutlu Aydemir</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/04/migration-from-struts2-to-struts6/paint-roller.webp&#34; alt=&#34;Upgrade your walls. Close up of a paint roller balanced on top of a blue paint can, at a 45 degree angle from the camera.&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by cottonbro studio: https://www.pexels.com/photo/a-paint-roller-on-the-paint-can-9222200/ --&gt;
&lt;p&gt;With the introduction of Struts 6, developers are provided enhanced features, security improvements, and modern practices that align with contemporary Java development. If you&amp;rsquo;re currently using Struts 2, migrating to Struts 6 is a worthwhile endeavor that can future-proof your application.&lt;/p&gt;
&lt;p&gt;Also, since Struts 2.5.x reached its &lt;a href=&#34;https://struts.apache.org/struts25-eol-announcement&#34;&gt;end of life&lt;/a&gt;, there won&amp;rsquo;t be any security updates for this version. This guide will walk you through the key differences between Struts 2 and Struts 6, including some significant changes in Struts 6 with practical configuration and code examples.&lt;/p&gt;
&lt;h3 id=&#34;getting-ready-for-migration-from-struts-2-to-struts-6&#34;&gt;Getting Ready for Migration from Struts 2 to Struts 6&lt;/h3&gt;
&lt;p&gt;Starting from Struts 6.0.0 the framework requires Java 8 at minimum. So, if you are running a Struts 2.x environment on an old version of Java, it needs to be upgraded to Java 8 at least. Check out the &lt;a href=&#34;https://cwiki.apache.org/confluence/display/WW/Version+Notes+6.0.0&#34;&gt;Struts 6.0.0 version notes&lt;/a&gt; for a list of changes.&lt;/p&gt;
&lt;h3 id=&#34;config-and-code-changes&#34;&gt;Config and Code Changes&lt;/h3&gt;
&lt;h4 id=&#34;servlet-api-dependency&#34;&gt;Servlet API Dependency&lt;/h4&gt;
&lt;p&gt;Struts 6.0.0 requires Servlet API 3.1 or newer and won&amp;rsquo;t work with the older versions. The Maven dependency that you can use for this is below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;javax.servlet&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;javax.servlet-api&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.1.0&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;deprecated-sitegraph-plugin-removed&#34;&gt;Deprecated Sitegraph Plugin Removed&lt;/h4&gt;
&lt;p&gt;Support for the sitegraph plugin, which was already deprecated, has now been removed entirely.&lt;/p&gt;
&lt;h4 id=&#34;velocity-plugin&#34;&gt;Velocity Plugin&lt;/h4&gt;
&lt;p&gt;Velocity support has been moved into a dedicated plugin. To continue using the Velocity plugin in your Struts application, you can extend the Struts config package definition, adding &lt;code&gt;velocity-default&lt;/code&gt; in &lt;code&gt;struts.xml&lt;/code&gt; as in the below example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;package&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mystrutsapp&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;extends=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;struts-default, velocity-default&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;dtd-of-strutsxml&#34;&gt;DTD of struts.xml&lt;/h4&gt;
&lt;p&gt;The proper DTD (Document Type Definition) header of struts.xml should be updated as below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;!DOCTYPE struts PUBLIC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;    &amp;#34;-//Apache Software Foundation//DTD Struts Configuration 6.0//EN&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;    &amp;#34;https://struts.apache.org/dtds/struts-6.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;class-changes&#34;&gt;Class Changes&lt;/h4&gt;
&lt;p&gt;There are some class changes you might need to consider.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;com.opensymphony.xwork2.config.providers.XmlConfigurationProvider&lt;/code&gt; was made abstract. You should now use &lt;code&gt;org.apache.struts2.config.StrutsXmlConfigurationProvider&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;com.opensymphony.xwork2.conversion.TypeConversionException&lt;/code&gt; was replaced by&lt;code&gt;org.apache.struts2.conversion.TypeConversionException&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;com.opensymphony.xwork2.XWorkException&lt;/code&gt; was replaced by &lt;code&gt;org.apache.struts2.StrutsException&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;constant-changes&#34;&gt;Constant Changes&lt;/h4&gt;
&lt;p&gt;Xwork config constants like &lt;code&gt;devMode&lt;/code&gt; were already deprecated and replaced with new constants like &lt;code&gt;struts.devMode&lt;/code&gt;. In Struts 6.0.0, the deprecated constants were completely removed. Also, new constants &lt;a href=&#34;https://struts.apache.org/core-developers/localization#search-in-default-bundles-first&#34;&gt;&lt;code&gt;struts.i18n.search.defaultbundles.first&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;https://struts.apache.org/tag-developers/tag-syntax#escaping-body-of-a-tag&#34;&gt;&lt;code&gt;struts.ui.escapeHtmlBody&lt;/code&gt;&lt;/a&gt; were introduced.&lt;/p&gt;
&lt;h4 id=&#34;ognl&#34;&gt;OGNL&lt;/h4&gt;
&lt;p&gt;Starting from Struts 6.0.0 OGNL expression length is limited to 256 characters by default as longer expressions can be &lt;a href=&#34;https://struts.apache.org/security/#apply-a-maximum-allowed-length-on-ognl-expressions&#34;&gt;considered harmful&lt;/a&gt;. However, the limit can be changed by adjusting the &lt;code&gt;struts.ognl.expressionMaxLength&lt;/code&gt; constant in struts.xml.&lt;/p&gt;
&lt;p&gt;Static method access is not possible using OGNL expressions like &lt;code&gt;@com.mypack.MyClass@MyStaticMethod()&lt;/code&gt;. This usage should be converted to alternative approaches such as Value Stack access through Action methods:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// MyAction.java&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyAction&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ActionSupport&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getMyData&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;MyStaticClass.&lt;span style=&#34;color:#369&#34;&gt;getMyStaticData&lt;/span&gt;();&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;execute&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;SUCCESS;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// MyClass.java&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyStaticClass&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getMyStaticData&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Data from static method&amp;#34;&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In your JSP you can access the data using an OGNL expression:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;s:property&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;myData&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;html-escaping&#34;&gt;HTML Escaping&lt;/h4&gt;
&lt;p&gt;Struts 6 uses a newer &lt;code&gt;FreeMarker&lt;/code&gt; version with auto-escaping enabled by default, affecting how output is rendered. You should avoid manual escaping (e.g., &lt;code&gt;?html&lt;/code&gt;) in FreeMarker templates when migrating to Struts 6.&lt;/p&gt;
&lt;p&gt;Struts 2 manual escaping:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;${user.name?html}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Struts 6 auto-escaping happens by default, so no &lt;code&gt;?html&lt;/code&gt; escape is necessary:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;${user.name}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;tiles-support&#34;&gt;Tiles Support&lt;/h3&gt;
&lt;p&gt;Until version 6.3.0, Struts 2 and Struts 6 had been supporting Tiles through &lt;a href=&#34;https://tiles.apache.org/&#34;&gt;Apache Tiles&lt;/a&gt;. The Apache Tiles project has been retired and is not updated anymore. The Struts team incorporated the Apache Tiles codebase into Struts and essentially added built-in support for Tiles starting with &lt;a href=&#34;https://cwiki.apache.org/confluence/display/WW/Version+Notes+6.3.0&#34;&gt;Struts version 6.3.0&lt;/a&gt; through the new &lt;a href=&#34;https://struts.apache.org/plugins/tiles/&#34;&gt;Tiles Plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are using Maven you can use the following dependency to setup the Tiles plugin in Struts 6.x:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.struts&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;struts2-tiles-plugin&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${version.tiles}&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then register the Tile listener in your &lt;code&gt;web.xml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;listener&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;listener-class&amp;gt;&lt;/span&gt;org.apache.struts2.tiles.StrutsTilesListener&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/listener-class&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/listener&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Migrating from Struts 2 to Struts 6 involves several significant changes due to updates in the framework&amp;rsquo;s architecture, dependencies, and requirements. Struts 6 (starting with version 6.0.0) introduces modernizations to align with newer Java versions, servlet specifications, and security practices.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Migration from Struts 1 to Struts 2</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2024/10/migration-from-struts1-to-struts2/"/>
      <id>https://www.endpointdev.com/blog/2024/10/migration-from-struts1-to-struts2/</id>
      <published>2024-10-04T00:00:00+00:00</published>
      <author>
        <name>Kürşat Kutlu Aydemir</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2024/10/migration-from-struts1-to-struts2/pexels-alfredinix-29415588-6894704.webp&#34; alt=&#34;A flock of flamingos fly against a pink and blue sunset&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Alfredinix: https://www.pexels.com/photo/flock-of-birds-flying-under-blue-sky-6894704/ --&gt;
&lt;p&gt;&lt;a href=&#34;https://struts.apache.org/&#34;&gt;Apache Struts&lt;/a&gt; is an open source web framework. Struts 1 was first released in 2000 and its latest version (1.3.10) was released in December 2008, marking the &lt;a href=&#34;https://struts.apache.org/struts1eol-announcement.html&#34;&gt;EOL of Struts 1&lt;/a&gt;. So Struts 1 is an ancient, dead version. Organizations still using it need to move away from Struts 1.&lt;/p&gt;
&lt;p&gt;Apache adapted the WebWork framework as Struts 2. Its architecture significantly differs from that of Struts 1. There are key points and obvious differences &lt;a href=&#34;https://struts.staged.apache.org/migration/&#34;&gt;provided by Apache&lt;/a&gt; to cover a migration plan from Struts 1 to Struts 2.&lt;/p&gt;
&lt;p&gt;The market share of Apache Struts is around &lt;a href=&#34;https://enlyft.com/tech/products/apache-struts&#34;&gt;0.01%&lt;/a&gt;, which gives relatively lower relevance to Apache Struts. However, there are companies still using Apache Struts 1 in their legacy applications, which may not be counted in market share statistics.&lt;/p&gt;
&lt;h3 id=&#34;a-comparison-of-struts-1-and-struts-2&#34;&gt;A Comparison of Struts 1 and Struts 2&lt;/h3&gt;
&lt;p&gt;Struts 1 and Struts 2 &lt;a href=&#34;https://struts.staged.apache.org/migration/#PAGE_14048&#34;&gt;differ significantly&lt;/a&gt; in their approach to handling web requests. Struts 1 follows a more rigid structure where &lt;code&gt;Action&lt;/code&gt; classes must extend an abstract base class, limiting flexibility. In contrast, Struts 2 is more dynamic, allowing Action classes to implement interfaces or even function as simple POJOs, enhancing adaptability. Struts 2 also includes the &lt;code&gt;ActionSupport&lt;/code&gt; class, which implements commonly used interfaces, making development easier and more versatile.&lt;/p&gt;
&lt;p&gt;The threading model in Struts 1 is based on a singleton pattern, meaning only one instance of an Action class exists to handle all requests, which requires careful management of thread safety. Struts 2, on the other hand, creates a new Action object for every request, which eliminates concerns about thread safety.&lt;/p&gt;
&lt;p&gt;When it comes to servlet dependency, Struts 1 is tightly coupled with the servlet API, requiring Actions to work directly with &lt;code&gt;HttpServletRequest&lt;/code&gt; and &lt;code&gt;HttpServletResponse&lt;/code&gt;. Struts 2 decouples the Action from the servlet container, using simple maps to represent the context. This allows for easier testing and more modular code. Struts 2 still offers the flexibility to access the request and response objects when necessary, but often through other architectural patterns that minimize the need for direct servlet interaction.&lt;/p&gt;
&lt;p&gt;Testing Struts 1 applications is challenging due to its tight coupling with the servlet API. External libraries like Struts &lt;code&gt;TestCase&lt;/code&gt; are often required to mock servlet objects for testing purposes. Struts 2 makes testing far more straightforward by supporting dependency injection and allowing Action classes to be tested in isolation, without needing complex mocks.&lt;/p&gt;
&lt;p&gt;Input handling in Struts 1 revolves around &lt;code&gt;ActionForm&lt;/code&gt; objects, which must extend a base class which often leads to redundancy. Struts 2 simplifies this by using Action properties directly, eliminating the need for separate &lt;code&gt;ActionForm&lt;/code&gt; objects. Additionally, Struts 2 supports complex object types for input and output, allowing domain specific objects to be used.&lt;/p&gt;
&lt;p&gt;In terms of expression languages, Struts 1 integrates with &lt;code&gt;JSTL&lt;/code&gt;, which provides basic object graph traversal but lacks robust support for collections. Struts 2 uses &lt;code&gt;OGNL&lt;/code&gt; (Object Graph Notation Language), offering more advanced and flexible expression capabilities, giving developers more control over data handling in views.&lt;/p&gt;
&lt;p&gt;Binding values into views is handled differently as well. Struts 1 uses standard JSP mechanisms, while Struts 2 introduces the &lt;code&gt;ValueStack&lt;/code&gt;, a system that decouples the view from the object being rendered. This allows for a greater degree of reuse and flexibility, especially when dealing with varying property types across different objects.&lt;/p&gt;
&lt;p&gt;Type conversion in Struts 1 is handled by Commons-Beanutils, which operates on a per-class basis. This can be limiting as it doesn’t allow for instance-level customization. Struts 2 improves on this by utilizing OGNL for type conversion, offering built-in converters for common types while also providing a flexible system for adding custom converters.&lt;/p&gt;
&lt;p&gt;Lastly, the control over action execution is much more granular in Struts 2. While Struts 1 uses a single request processor per module, Struts 2 introduces Interceptor Stacks, allowing developers to define different lifecycles for individual Actions. This customization helps tailor the flow of execution to fit specific application requirements.&lt;/p&gt;
&lt;p&gt;Actions are central components in both Struts 1 and 2 and play a major role in the architectures of both Struts versions. Struts 2 offers a more modern, flexible, and test-friendly framework, improving upon Struts 1’s limitations by embracing POJO actions, better type conversion, enhanced validation, and a decoupled threading model. These changes make Struts 2 more adaptable and easier to maintain, particularly for complex web applications.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can see more about request processing flow in Struts 2 in &lt;a href=&#34;https://www.infoq.com/articles/converting-struts-2-part1/&#34;&gt;this InfoQ article&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;migration&#34;&gt;Migration&lt;/h3&gt;
&lt;h4 id=&#34;dependencies&#34;&gt;Dependencies&lt;/h4&gt;
&lt;p&gt;Add the Struts 2.0 Jars to the existing Struts 1.3 application.  The latest Jar files can be downloaded from the &lt;a href=&#34;https://struts.apache.org/download.cgi&#34;&gt;Apache Struts download page&lt;/a&gt;. Alternatively, you can download the Jar files for any of the previous versions from the &lt;a href=&#34;https://archive.apache.org/dist/struts/&#34;&gt;archive&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;request-handling&#34;&gt;Request handling&lt;/h4&gt;
&lt;p&gt;Configure Struts 2 to handle requests with the &lt;code&gt;*.action&lt;/code&gt; extension, differently from Struts 1, which uses the &lt;code&gt;*.do&lt;/code&gt; extension.&lt;/p&gt;
&lt;h4 id=&#34;configure-web-application&#34;&gt;Configure web application&lt;/h4&gt;
&lt;p&gt;The web application is enabled in the &lt;code&gt;web.xml&lt;/code&gt; configuration file. There are a few changes to this file needed for Struts 2. Dispatcher configuration is done by simply renaming the configuration tags from &lt;code&gt;&amp;lt;servlet&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;servlet-mapping&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;filter&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;filter-mapping&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A very basic &lt;code&gt;web.xml&lt;/code&gt; configuration for Struts 1 would look like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;servlet&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;servlet-name&amp;gt;&lt;/span&gt;action&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/servlet-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;servlet-class&amp;gt;&lt;/span&gt;org.apache.struts.action.ActionServlet&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/servlet-class&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;init-param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;param-name&amp;gt;&lt;/span&gt;config&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/param-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;param-value&amp;gt;&lt;/span&gt;/WEB-INF/struts-config.xml&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/param-value&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/init-param&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;load-on-startup&amp;gt;&lt;/span&gt;2&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/load-on-startup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/servlet&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;servlet-mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;servlet-name&amp;gt;&lt;/span&gt;action&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/servlet-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;*.do&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/servlet-mapping&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The corresponding conversion to Struts 2 would look like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;filter&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;filter-name&amp;gt;&lt;/span&gt;webwork&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/filter-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;filter-class&amp;gt;&lt;/span&gt;org.apache.struts.action2.dispatcher.FilterDispatcher&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/filter-class&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/filter&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;filter-mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;filter-name&amp;gt;&lt;/span&gt;webwork&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/filter-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/*&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/filter-mapping&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;migrating-actions&#34;&gt;Migrating actions&lt;/h4&gt;
&lt;p&gt;A general structure of a Struts 1 action class is below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;CustomAction&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Action&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ActionForward&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;execute&lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;ActionMapping&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;mapping,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;ActionForm&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;form,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;HttpServletRequest&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;request,&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;HttpServletResponse&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;response)&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throws&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Exception&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// add logic&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(mapping.&lt;span style=&#34;color:#369&#34;&gt;findForward&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;));&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;    &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Struts 1 actions are singletons and extend the base &lt;code&gt;Action&lt;/code&gt; class. Alternatively, &lt;code&gt;DispatchAction&lt;/code&gt; can be used instead of the base class. In either case, the &lt;code&gt;execute&lt;/code&gt; method is the entry point to an Action class. Actions have to be thread-safe and hence all the necessary variables should be handled in method scope. Finally, the &lt;code&gt;execute&lt;/code&gt; method returns an &lt;code&gt;ActionForward&lt;/code&gt; response.&lt;/p&gt;
&lt;p&gt;On the other hand, in Struts 2 a basic action class structure would look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyAction&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;ActionSupport&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;   &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;String&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;execute&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;throws&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Exception&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// add logic&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;success&amp;#34;&lt;/span&gt;;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;   &lt;/span&gt;}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A Struts 2 action class will usually (but not necessarily) extend &lt;code&gt;ActionSupport&lt;/code&gt;. The usual entry point is again the &lt;code&gt;execute&lt;/code&gt; method—however, this method has no parameters. In Struts 2 action classes are not singletons. Instead an instance of the action class is created for each request, so class scope variables can be used safely, alleviating many thread-safety concerns in Struts 1 actions. Struts 2 will inject a &lt;code&gt;HttpServletRequest&lt;/code&gt; for the current request into actions implementing the &lt;code&gt;ServletRequestAware&lt;/code&gt; interface, allowing you access to the underlying request object.&lt;/p&gt;
&lt;h4 id=&#34;action-configuration&#34;&gt;Action Configuration&lt;/h4&gt;
&lt;p&gt;In Struts 1 applications, action configurations are made in &lt;code&gt;struts-config.xml&lt;/code&gt; located in the &lt;code&gt;WEB-INF&lt;/code&gt; directory. Struts 1 applications need to configure actions and action form beans in this configuration file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;struts-config&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;form-beans&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;form-bean&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;myForm&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;com.endpoint.blog.struts.MyForm&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/form-beans&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;global-forwards&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;forward&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;home&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;path=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/home.do&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;redirect=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;forward&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;projects&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;path=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/myactionProcess.do?dispatch=myDispatch&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;redirect=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/global-forwards&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;action-mappings&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;action&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;path=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/myPackage/add&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;org.apache.struts.actions.ForwardAction&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/action-mappings&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/struts-config&amp;gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Struts 2 configuration involves a package wrapper around the action definitions.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;struts&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;package&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;myPackage&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;extends=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;struts-default&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;action&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;add&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;result&amp;gt;&lt;/span&gt;/myPackage/add.jsp&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/result&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/action&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/struts&amp;gt;&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, in Struts 2, actions can be defined in a more general way using Struts conventions:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;struts&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;constant&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;struts.action.extension&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ep&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;constant&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;struts.convention.action.packages&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;com.endpoint.blog.actions.*&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/struts&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this article we covered migrating the most basic components from Struts 1 to Struts 2 and compared several features between each version. Complete configuration and code migration from Struts 1 to Struts 2 can be done gradually, involving actions, interceptors, tags, and many other details. Not completely straightforward, but the patterns and convention changes adopted in Struts 2 become clearer as you dive in.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Migrating Rails 6 React to Rails 7 React</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2023/06/migrating-rails6-react-rails7-react/"/>
      <id>https://www.endpointdev.com/blog/2023/06/migrating-rails6-react-rails7-react/</id>
      <published>2023-06-26T00:00:00+00:00</published>
      <author>
        <name>Indra Pranesh Palanisamy</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2023/06/migrating-rails6-react-rails7-react/pexels-indra-pranesh-palanisamy-17019134.webp&#34; alt=&#34;A coconut tree stands in the corner of a serene blue sky&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Indra Pranesh Palanisamy, 2022 --&gt;
&lt;p&gt;CasePointer’s disease reporting portal is built on React and Rails 6, and it&amp;rsquo;s time for an upgrade to Rails 7. This blog post will cover the steps, benefits, and challenges of migrating from Rails 6 to Rails 7, and offer valuable insights into the world of Ruby on Rails.&lt;/p&gt;
&lt;p&gt;With the recent release of Rails 7, there are many new features and improvements to explore.
One of the biggest changes in Rails 7 is the retirement of Webpacker in favor of using the native webpack for bundling JavaScript.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For those who are not familiar, Webpacker is a Rails gem which is a wrapper around the webpack build system that provides a standard webpack configuration and reasonable defaults.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;steps-for-migrating-rails-6-react-to-rails-7-react&#34;&gt;Steps for migrating Rails 6 React to Rails 7 React&lt;/h3&gt;
&lt;p&gt;To migrate a Rails 6 React application to Rails 7 React, follow these steps:&lt;/p&gt;
&lt;h4 id=&#34;1-update-the-rails-gem-in-the-gemfile&#34;&gt;1. Update the Rails Gem in the Gemfile&lt;/h4&gt;
&lt;p&gt;In your application&amp;rsquo;s Gemfile, update the Rails gem version to Rails 7:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-gem &amp;#34;rails&amp;#34;, &amp;#34;~&amp;gt; 6.1.4&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;background-color:#dfd&#34;&gt;+gem &amp;#34;rails&amp;#34;, &amp;#34;~&amp;gt; 7.0.0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;2-upgrade-rails-packages&#34;&gt;2. Upgrade Rails packages&lt;/h4&gt;
&lt;p&gt;Upgrade the Rails packages using Yarn:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;yarn upgrade @rails/actioncable --latest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;yarn upgrade @rails/activestorage --latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;3-run-the-rails-update-task&#34;&gt;3. Run the Rails update task&lt;/h4&gt;
&lt;p&gt;Run the following command to initiate the Rails update task:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/rails app:update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This task guides you file by file to integrate the new Rails 7 defaults. Refer to the &lt;a href=&#34;https://guides.rubyonrails.org/upgrading_ruby_on_rails.html&#34;&gt;Rails documentation&lt;/a&gt; for detailed information on this update task.&lt;/p&gt;
&lt;h4 id=&#34;4-remove-webpacker&#34;&gt;4. Remove Webpacker&lt;/h4&gt;
&lt;p&gt;Since Webpacker is no longer the default in Rails 7, follow these steps to remove it:&lt;/p&gt;
&lt;p&gt;Remove the webpacker gem from your Gemfile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#000;background-color:#fdd&#34;&gt;-gem &amp;#39;webpacker&amp;#39;, &amp;#39;~&amp;gt; 4.0&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Remove the &lt;code&gt;webpacker.yml&lt;/code&gt; file and any other files associated with webpacker.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;bundle install&lt;/code&gt; to update the Gemfile.lock:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bundle install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;5-setting-up-webpack&#34;&gt;5. Setting up Webpack&lt;/h4&gt;
&lt;p&gt;To set up Webpack for your Rails 7 React application, follow these steps:&lt;/p&gt;
&lt;p&gt;Install webpack and other necessary libraries:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;yarn install webpack webpack-cli @babel/core&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create a webpack.config.js file at the root of your application:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// webpack.config.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; path = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; webpack = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;webpack&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Add other necessary plugins and configurations
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;module.exports = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// Webpack configuration options
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  output: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Make sure to use the path of the rails asset pipeline
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    path: path.join(__dirname, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/app/assets/builds&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  module: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    rules: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#888&#34;&gt;// Add CSS/SASS/SCSS rule with loaders
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test: &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\.(?:sa|sc|c)ss$/i&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        use: [MiniCssExtractPlugin.loader, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;css-loader&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sass-loader&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test: &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\.(png|jpe?g|gif|eot|woff2|woff|ttf|svg)$/i&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        use: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;file-loader&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        test: &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/\.(js|jsx)$/&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        exclude: &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/node_modules/&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        use: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          loader: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;babel-loader&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add a .babelrc file for Babel configuration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;presets&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;@babel/preset-env&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;@babel/preset-react&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Update the scripts section in your package.json:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;webpack&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;webpack --watch&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;6-update-rails-asset-tags&#34;&gt;6. Update Rails asset tags&lt;/h4&gt;
&lt;p&gt;In your application views, update the asset tags from &lt;code&gt;stylesheet_pack_tag&lt;/code&gt; and &lt;code&gt;javascript_pack_tag&lt;/code&gt; to &lt;code&gt;stylesheet_link_tag&lt;/code&gt; and &lt;code&gt;javascript_include_tag&lt;/code&gt;, respectively:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;%= stylesheet_link_tag &amp;#39;application&amp;#39;, media: &amp;#39;all&amp;#39;, &amp;#39;data-turbolinks-track&amp;#39;: &amp;#39;reload&amp;#39; %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;%= javascript_include_tag &amp;#39;application&amp;#39;, &amp;#39;data-turbolinks-track&amp;#39;: &amp;#39;reload&amp;#39; %&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;7-start-the-dev-server&#34;&gt;7. Start the dev server&lt;/h4&gt;
&lt;p&gt;Start the Rails development server with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rails server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then start the React development server with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;yarn run dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By following these steps, we can take advantage of the new features and improvements in Rails 7 while continuing to leverage the power of React.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Data Migration Tips</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2023/02/migration-tips-and-tricks/"/>
      <id>https://www.endpointdev.com/blog/2023/02/migration-tips-and-tricks/</id>
      <published>2023-02-04T00:00:00+00:00</published>
      <author>
        <name>Josh Tolley</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2023/02/migration-tips-and-tricks/leaves.webp&#34; alt=&#34;Scattered leaves on grass fill the frame, made up of many colors; green, cyan, pale and bright yellow, with red leaves providing highlights&#34;&gt;&lt;/p&gt;
&lt;!-- Photo by Seth Jensen, 2022 --&gt;
&lt;p&gt;When you&amp;rsquo;re in the business of selling software to people, you tend to get a few chances to migrate data from their legacy software to your shiny new system. Most recently for me that has involved public health data exported from legacy disease surveillance systems into &lt;a href=&#34;/expertise/postgresql/&#34;&gt;PostgreSQL databases&lt;/a&gt; for use by the open source EpiTrax system and its companion EMSA.&lt;/p&gt;
&lt;p&gt;We have collected a few tips that may help you learn from our successes,
as well as our &lt;del&gt;mistakes&lt;/del&gt;particularly educational experiences.&lt;/p&gt;
&lt;h3 id=&#34;customer-management&#34;&gt;Customer Management&lt;/h3&gt;
&lt;p&gt;Your job is to satisfy your customers, and &lt;strong&gt;your customers want to know how
the migration is progressing&lt;/strong&gt;. Give them an answer, even if it&amp;rsquo;s just a
generalization. This may be a burndown chart, a calculated percentage, a nifty
graphic, or whatever, but something your project managers can show to their
managers, to know more or less how far along things are.&lt;/p&gt;
&lt;p&gt;Your job is also to know your system; that&amp;rsquo;s not the customer&amp;rsquo;s job. They
shouldn&amp;rsquo;t have to get their data into a specific format for you to make use of
it. &lt;strong&gt;Be as flexible as possible&lt;/strong&gt; in the data format and structure
you&amp;rsquo;ll accept. In theory, so long as your customer can provide the legacy data
in a machine-readable format, you should be able to use it. Let them focus on
getting the data out of their legacy system — which is sometimes quite an
effort in itself! Real data is almost always messy data, and your customer will
probably want to take the opportunity to clean things up; make it as easy as
possible for them to do that, while still ensuring the migration proceeds
quickly and smoothly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be careful about the vocabulary you use&lt;/strong&gt; with your customer. Your system and
the legacy system probably deal with the same kinds of data, and do generally
the same kinds of things. Of course, your software does it better than the
old &amp;rsquo;n&amp;rsquo; busted mess you&amp;rsquo;re replacing, but in order to be better, your software
has to be different from what it&amp;rsquo;s replacing. Different software means
different methods, different processes, and different concepts. You and your
customer might use the same words to mean totally different things, and unless
you&amp;rsquo;re careful, those differences can remain hidden well into the
implementation process, coming to light just in time to highlight some horrible
mistake you&amp;rsquo;ve made. Try to avoid that. Talk with your customer to make sure
your team and their team share a common vocabulary. If your system&amp;rsquo;s core
function is to track widgets, and their legacy system&amp;rsquo;s function is also to
track widgets, make sure you understand the differences between your software&amp;rsquo;s
concept of a widget and their legacy system&amp;rsquo;s concept of a widget.&lt;/p&gt;
&lt;h3 id=&#34;migration-scripting&#34;&gt;Migration Scripting&lt;/h3&gt;
&lt;p&gt;For our products and our customers, we&amp;rsquo;ve found pretty much every migration
process is different, and each one is a custom programming job. Your team
should &lt;strong&gt;decide at the beginning what steps the migration needs to include,
and what technologies it will use&lt;/strong&gt; in each step. Here at End Point, we often
like to work directly in the database, in SQL. Migrations are all about
manipulating data, and SQL is well suited for that task. With whatever
technology you use, decide how you&amp;rsquo;ll use it, to manage the considerations
given here. You can change your mind later and refactor accordingly, but always
have a plan you&amp;rsquo;re following.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Design the migration as a sequence of processes.&lt;/strong&gt; That is, first you might
import one type of record, next another type of record that depends on the
previous one, followed by several further steps to import data from a third
source, clean it, map values from the legacy system to the new system, validate
the results, and create records in the destination database. Of course the
steps will vary from project to project, but the point is your migration will
probably include several steps which need to be run in a specific order, so
plan your development conventions accordingly. At End Point, we often like to
&lt;strong&gt;put each step in a SQL file, and name each file beginning with a number&lt;/strong&gt;, so
you can run each script in order sorted by filename, and achieve the correct
result. We might have files called:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;01_import_products.sql&lt;/li&gt;
&lt;li&gt;02_import_customers.sql&lt;/li&gt;
&lt;li&gt;03_import_order_history.sql&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s also common to implement each step one at a time, and to need to run each
step several times as it&amp;rsquo;s being developed. We find it very helpful to &lt;strong&gt;wrap
each step in a transaction&lt;/strong&gt;. Often that means each SQL file begins with a
&lt;code&gt;BEGIN;&lt;/code&gt; statement, and ends with &lt;code&gt;COMMIT;&lt;/code&gt;. Often I&amp;rsquo;ll leave out the &lt;code&gt;COMMIT&lt;/code&gt;
until I&amp;rsquo;m finished working on a file. That way to work on the code I can open a
database session and run the migration script I&amp;rsquo;m working on, and when it
completes, it will leave me inside the open transaction where I can inspect the
results of my work. I make changes to the script, roll back the transaction,
and run the script again, for as many iterations as it takes. I only add a
&lt;code&gt;COMMIT&lt;/code&gt; when I&amp;rsquo;ve tested the whole file and think it&amp;rsquo;s ready for the next step
in testing.&lt;/p&gt;
&lt;p&gt;I mentioned above that the customer may want to use this opportunity to clean
their data. You should want this, too. &lt;strong&gt;Make sure the data you&amp;rsquo;re feeding your
new system is as clean and well-structured as possible.&lt;/strong&gt; You may find, as we
do, that most of the work in your migrations is in validating the input data,
and that actually creating new records in your application is almost an
afterthought. That&amp;rsquo;s OK. You may also find there are places where your
application, wonderful though it may be, could stand to be more strict about
the data it accepts. I have often discovered my application&amp;rsquo;s database needs a uniqueness
constraint, or a foreign key, thanks to a migration I was working on.&lt;/p&gt;
&lt;h3 id=&#34;data-migration-history&#34;&gt;Data Migration History&lt;/h3&gt;
&lt;p&gt;I wish I could truthfully claim all our migrations go off flawlessly, but that
would be a lie. It&amp;rsquo;s not unheard of to run into some corner case, a few weeks
or even months after the migration goes live, where data wasn&amp;rsquo;t migrated correctly.&lt;/p&gt;
&lt;p&gt;On the other hand, it&amp;rsquo;s certainly not uncommon for a customer or coworker to
spot something that strikes them as odd, after the migration goes live, only to
find later that everything was in fact correct. In either case, it&amp;rsquo;s important
to &lt;strong&gt;preserve a history of the migration&lt;/strong&gt; to investigate these concerns. We
accomplish this with a few specific steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a database schema for the migration, and a table within that schema for each data file or object type we&amp;rsquo;re importing.&lt;/strong&gt; I call these tables &amp;ldquo;staging tables&amp;rdquo;, where the incoming data is &amp;ldquo;staged&amp;rdquo; as it&amp;rsquo;s cleaned and validated. Having a separate schema means these tables can remain in the production database long after the migration is complete, generally without interfering with anything.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;These staging tables should generally &lt;strong&gt;use text fields, to be as forgiving and flexible as possible with the incoming data.&lt;/strong&gt; We can clean, parse, and reformat the data after it&amp;rsquo;s imported.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Don&amp;rsquo;t change the data in these staging tables; add to the data instead.&lt;/strong&gt; In other words, if you need to map a value from the legacy system to a different value for your new system, don&amp;rsquo;t change the column you imported into the staging table; instead, add a new column to the staging table where you&amp;rsquo;ll store the re-mapped value. If you need to parse a text field into a date (because you followed the instruction to use text fields!), don&amp;rsquo;t change the type of an imported column; instead, add a new column of date or timestamp type, to store the parsed value. That way, when three months down the road someone discovers that some of the imported records have weird dates, you have all the information you need to determine whether the fault lies with the imported data or some step of the migration progress. Knowing exactly where the fault crept in leaves you that much more empowered to fix it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep track of your migrated records&amp;rsquo; primary keys, in the legacy system and the new system.&lt;/strong&gt; Imagine you&amp;rsquo;ve just imported your client&amp;rsquo;s legacy customer list into a staging table. This data includes the legacy system&amp;rsquo;s primary key. Add a new column to the table for your new system&amp;rsquo;s primary key, and populate it. Many of our systems use an integer sequence as a primary key, so we&amp;rsquo;d add a new integer column to the staging table, and fill it with the next values from the sequence. Following this principle will give you several important abilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can always connect a record in the legacy system with its corresponding record(s) in the new system. If you&amp;rsquo;ve imported a customer list in this fashion, then when you&amp;rsquo;re importing the order data later, and each order points to a customer using a legacy customer primary key, you can easily find the correct customer primary key to use in your system.&lt;/li&gt;
&lt;li&gt;You can easily know if a record in your system comes from the migration, or from normal day-to-day business. You will probably use this every time you try to debug something with your migration.&lt;/li&gt;
&lt;li&gt;If you need to remove all imported records and re-import them, you can identify exactly which records those are. This should be only rarely needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, &lt;strong&gt;document the decision making process, in comments directly in your
code.&lt;/strong&gt; For instance, if you have a table of mappings from one value to
another, chances are good you arrived at the final version of that mapping
table only after some discussion with the customer. Chances are also good
someone&amp;rsquo;s going to question it later on. It&amp;rsquo;s helpful to keep a comment around,
something like, &lt;code&gt;# Joe Rogers verified this is the correct mapping in the daily standup meeting, 23 Nov 2022&lt;/code&gt;. This is especially common if you
eventually decide to ignore a certain class of records. &lt;code&gt;/* Rebecca says ignore all records with type = &amp;quot;ARCHIVED&amp;quot;, via group email 9 Jan 2023 */&lt;/code&gt; is a very
helpful clue when someone comes around wondering where those records went.&lt;/p&gt;
&lt;h3 id=&#34;teamwork&#34;&gt;Teamwork&lt;/h3&gt;
&lt;p&gt;My remaining tips apply to almost any programming project. First, &lt;strong&gt;use source
control, and commit your code to it often.&lt;/strong&gt; I can&amp;rsquo;t count how often I&amp;rsquo;ve been
grateful the Git repository had a backup of my work, or made my work accessible
to fill some unexpected need on some other system, nor can I count how many
times I&amp;rsquo;ve been stuck because someone else didn&amp;rsquo;t commit their code so I
couldn&amp;rsquo;t get at it when I needed it.  Let&amp;rsquo;s not talk about how many times I&amp;rsquo;ve
caused someone else to get stuck in the same way&amp;hellip; Of course, don&amp;rsquo;t commit
your customer&amp;rsquo;s data. But you should commit your code, and commit it often.&lt;/p&gt;
&lt;p&gt;Finally, where possible, &lt;strong&gt;work with someone else.&lt;/strong&gt; Two programmers reviewing
each other&amp;rsquo;s code and collaborating on solutions are often far better than two
programmers working alone, or one programmer working twice as much.&lt;/p&gt;
&lt;p&gt;Speaking of working out solutions together, I&amp;rsquo;d love your help improving this
list. What keys have you found are important for data migration projects? I
welcome your comments. And if you&amp;rsquo;re looking for someone to handle a data
migration project for you, &lt;a href=&#34;/contact/&#34;&gt;give us a call&lt;/a&gt;!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Salesforce data migration: promoting data changes from a sandbox to production</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/11/salesforce-data-migration/"/>
      <id>https://www.endpointdev.com/blog/2021/11/salesforce-data-migration/</id>
      <published>2021-11-24T00:00:00+00:00</published>
      <author>
        <name>Dylan Wooters</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/11/salesforce-data-migration/manhattan-upper-skyline.jpg&#34; alt=&#34;Manhattan skyline&#34;&gt;
Photo by Dylan Wooters, 2021&lt;/p&gt;
&lt;h3 id=&#34;intro&#34;&gt;Intro&lt;/h3&gt;
&lt;p&gt;End Point recently completed a &lt;a href=&#34;https://www.eiffeltrading.com/&#34;&gt;new e-commerce website&lt;/a&gt; built using Nuxt, Node.js, and Algolia and backed by Salesforce data. As part of the project, the client also wanted to re-categorize their products. They had already updated the data in a Salesforce sandbox and needed a way to move their changes to production in conjunction with the launch of the new website.&lt;/p&gt;
&lt;p&gt;This data migration initially seemed like a simple request, but it ended up being one of the more challenging parts of the project. While there are clear instructions on how to migrate &lt;em&gt;code&lt;/em&gt; changes from a sandbox to production (via &lt;a href=&#34;https://help.salesforce.com/s/articleView?id=sf.changesets.htm&amp;amp;type=5&#34;&gt;change sets&lt;/a&gt;), there is very little information on how to migrate &lt;em&gt;data&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here I will outline the solution we developed, which involved writing a custom migration script using the &lt;a href=&#34;https://jsforce.github.io/&#34;&gt;JSForce&lt;/a&gt; library, testing using additional sandboxes, and planning for a rollback. This can serve as a blueprint for developers faced with the same task.&lt;/p&gt;
&lt;h3 id=&#34;why-not-use-data-loader-to-migrate&#34;&gt;Why not use Data Loader to migrate?&lt;/h3&gt;
&lt;p&gt;The Salesforce &lt;a href=&#34;https://developer.salesforce.com/docs/atlas.en-us.234.0.dataLoader.meta/dataLoader/data_loader.htm&#34;&gt;Data Loader&lt;/a&gt; is a clunky yet reliable application that allows you to load large amounts of data directly into a Salesforce org. It will work to migrate data if the requirements are not complex. For example, if you only need to load data into a few objects and there are no connected objects or parent-child relationships. We chose not to use Data Loader for a few important reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The data we were migrating was complex. Three levels of hierarchical categories were stored in Salesforce as separate custom objects, attached to another custom object representing products. To make things worse, the category objects were also all attached to each other in a parent-child hierarchy. To load this data using the Data Loader and CSV files was very time-consuming and error-prone.&lt;/li&gt;
&lt;li&gt;We needed a fast and reproducible migration process. We had a strict cutover time for the launch of the new website, and the migration therefore had to run quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We did end up using Data Loader as part of our rollback process, but more on that in step 6.&lt;/p&gt;
&lt;h3 id=&#34;our-solution&#34;&gt;Our solution&lt;/h3&gt;
&lt;h4 id=&#34;1-export-json-data-from-the-sandbox&#34;&gt;1. Export JSON data from the sandbox&lt;/h4&gt;
&lt;p&gt;For our source data, I exported JSON from the Salesforce sandbox using the &lt;a href=&#34;https://jsforce.github.io/start/#command-line-interface&#34;&gt;JSForce CLI&lt;/a&gt;, specifically the &lt;a href=&#34;https://jsforce.github.io/document/#query&#34;&gt;query&lt;/a&gt; function. Having the source data in JSON was easier than parsing CSV files exported directly from Salesforce. I committed the query commands to our Git repo for easy access during the actual production migration.&lt;/p&gt;
&lt;p&gt;Here is a basic example of how to export data using the JSForce CLI.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jsforce -c dylan@endpointdev.com \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -e &amp;#34;query(&amp;#39;SELECT Id, Name, CreatedDate FROM Account&amp;#39;)&amp;#34; &amp;gt; sandbox-export.json&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;2-write-a-custom-migration-script&#34;&gt;2. Write a custom migration script&lt;/h4&gt;
&lt;p&gt;We were already using JSForce in our project to sync Salesforce data with the Algolia search engine, so we decided to also use the library to migrate data. I won&amp;rsquo;t dive into the specifics of how to use JSForce in this article, since we have &lt;a href=&#34;/blog/2020/03/salesforce-integration-with-node/&#34;&gt;another post&lt;/a&gt; that does that well.&lt;/p&gt;
&lt;p&gt;My basic approach to writing the script started with asking the question &amp;ldquo;What would we do if we had direct access to the Salesforce database&amp;rdquo;? Once I had all of the set-based INSERT and UPDATE operations mapped out, I then translated that into procedural TypeScript code.&lt;/p&gt;
&lt;p&gt;Here is a basic example from the migration script, where we are reading data from the JSON exported from the sandbox and loading it into Salesforce production.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; * as fs from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; * as path from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt; * as jsforce from &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;jsforce&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; openConnection = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;async&lt;/span&gt;() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; conn = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; jsforce.Connection({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    loginUrl : process.env.instanceURL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; conn.login(process.env.username as string, process.env.password as string);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  console.log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;info&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`Connected to Salesforce.`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; conn;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; getJson = (filename: string) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; JSON.parse(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    fs.readFileSync(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      path.resolve(__dirname, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sandbox-data/&amp;#39;&lt;/span&gt; + filename)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ).toString()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; segmentData = getJson(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;new-segments.json&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; connection = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; openConnection();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; i = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;; i &amp;lt; segmentData.records.length; i++) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; segment = segmentData.records[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// Ignore Marine Equipment segment since it already exists
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (segment.Name != &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Marine Equipment&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; connection.sobject(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Segment__c&amp;#39;&lt;/span&gt;).create({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Name: segment.Name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Description__c: segment.Description__c,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Meta_Description__c: segment.Meta_Description__c,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Meta_Keywords__c: segment.Meta_Keywords__c,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      SubHeader__c: segment.SubHeader__c
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;3-come-up-with-a-plan-for-testing-data-changes&#34;&gt;3. Come up with a plan for testing data changes&lt;/h4&gt;
&lt;p&gt;After the migration script runs, you&amp;rsquo;ll need a way to verify that the data was loaded successfully and according to the requirements. For us, this meant working with our client to develop a smoke testing plan. After running our script, we switched a test version of the website to run against the migrated data in a Salesforce sandbox, and then used the plan to test all of the website functionality to make sure everything worked and the data looked correct. You&amp;rsquo;ll want to develop a plan based on your specific use case, whether that means verifying changes in Salesforce or an integrated system/​website.&lt;/p&gt;
&lt;h4 id=&#34;4-test-the-migration-script-against-a-copy-of-production&#34;&gt;4. Test the migration script against a copy of production&lt;/h4&gt;
&lt;p&gt;Once you have the migration script and test plan completed, you&amp;rsquo;ll want to run the script against a copy of Salesforce production. The easiest way to do this is to create a new Full sandbox in Salesforce. However, if you&amp;rsquo;re short on Full sandbox licenses, you can create a Partial Copy, choosing only the options that are targeted in your script. Both sandboxes can be created by navigating to the Setup page in Salesforce, and then going to Platform Tools &amp;gt; Environments &amp;gt; Sandboxes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/11/salesforce-data-migration/salesforce-sandboxes.png&#34; alt=&#34;Salesforce sandboxes&#34;&gt;&lt;/p&gt;
&lt;p&gt;After the script runs successfully, you can evaluate the data in the target sandbox to make sure everything looks correct. In our case, this involved pointing a test version of the new site to the migrated data.&lt;/p&gt;
&lt;p&gt;One important caveat in this step is the refresh interval for the sandbox. Full sandboxes have a refresh interval of 30 days, and partials have an interval of 5 days. Essentially what this means is that you can&amp;rsquo;t easily press a button and refresh the sandbox after running the migration script, in case you need to make a change or fix an error. As a result, it&amp;rsquo;s best to have the data exported from Salesforce production and understand how to rollback the script updates, which I&amp;rsquo;ll explain more later on.&lt;/p&gt;
&lt;h4 id=&#34;5-export-data-from-salesforce-production&#34;&gt;5. Export data from Salesforce production&lt;/h4&gt;
&lt;p&gt;Obtain a copy of the Salesforce production data by using the Data Export feature. This can be used as source data for a rollback, if errors occur in either testing the migration script or the real run against production.&lt;/p&gt;
&lt;p&gt;To export data from Salesforce, first make a list of all the objects that will be affected by the migration. Then, navigate to the Salesforce Setup page, and go to Administration &amp;gt; Data &amp;gt; Data Export. Click the &amp;ldquo;Export Now&amp;rdquo; button and choose the applicable objects (or choose &amp;ldquo;Include All Data&amp;rdquo;), and hit &amp;ldquo;Start Export&amp;rdquo;. The data export will take a bit, but you should get an email from Salesforce once it is complete.&lt;/p&gt;
&lt;h4 id=&#34;6-perform-a-test-rollback&#34;&gt;6. Perform a test rollback&lt;/h4&gt;
&lt;p&gt;After you test the migration script, if you find that the data is incorrect, you can perform a rollback using the Salesforce Data Loader. Even if the script runs successfully, you&amp;rsquo;ll want to test a rollback in case an error occurs during the real run. Here&amp;rsquo;s how I did it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://developer.salesforce.com/docs/atlas.en-us.234.0.dataLoader.meta/dataLoader/loader_install_general.htm&#34;&gt;Download and install&lt;/a&gt; the Data Loader for your platform&lt;/li&gt;
&lt;li&gt;Delete all the data for each affected object. To do this, log into Salesforce and open the Developer Console, then click on the &amp;ldquo;Debug&amp;rdquo; menu and choose &amp;ldquo;Open execution anonymous window&amp;rdquo;. Use the following code snippet to delete all data, replacing the &lt;code&gt;Yourobject__c&lt;/code&gt; with the name of the target object. Again, you&amp;rsquo;ll need to do this for each object that was affected by your script.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;List&amp;lt;Yourobject__c&amp;gt; SobjLst = [select id from Yourobject__c];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;delete SobjLst;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Finally, re-insert the data using the CSV backups from step 4. In the Data Loader, choose the Insert method, and log into Salesforce. Be sure to use an account with the proper write permissions. Then go through the Data Loader wizard, choosing the applicable CSV backup file and load the data back into Salesforce. Repeat for each affected object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/11/salesforce-data-migration/salesforce-data-loader.png&#34; alt=&#34;Salesforce Data Loader&#34;&gt;
&lt;br/&gt;&lt;small&gt;Not the prettiest application, at least on a Mac. Circled in red is the Insert button.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;This process turned out to be quite cumbersome for our project, due to the complexity of the data. It ended up taking several rounds of manual work in Excel (vlookups) to align all of the connected object ids correctly. It took me back to my first job after college. Hopefully your attempt will be easier! If it proves to be challenging, check out the &lt;a href=&#34;https://dataloader.io/&#34;&gt;online version&lt;/a&gt; of the Data Loader. It has expanded features that make it easier to import objects with relationships. Note, however, that the online version is only free for up to 10,000 records.&lt;/p&gt;
&lt;h4 id=&#34;7-run-the-script-against-production&#34;&gt;7. Run the script against production&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/11/salesforce-data-migration/slack-rumble.png&#34; alt=&#34;Launch in slack&#34;&gt;
&lt;br/&gt;&lt;small&gt;Starting the &amp;ldquo;livestream&amp;rdquo; of our website launch in Slack!&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Finally it&amp;rsquo;s time for the main event. Before you run your script against Salesforce production, be sure to export both the data from the sandbox and the data from Production (steps 1 and 4). This will ensure that the most recent data gets migrated and will set you up for a rollback if something goes wrong.&lt;/p&gt;
&lt;p&gt;After your migration script runs without errors, use the testing plan from step 3 to verify that the data is correct. Once you&amp;rsquo;ve verified everything, give yourself a pat on the back for successfully navigating the arcane and treacherous world of a Salesforce data migration!&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;If your Salesforce data model is complex, consider using the steps above to migrate data from your sandbox to production. It will ensure a successful migration, especially if need a fast and repeatable process. If your data is simple and you are migrating a few objects, check out the Salesforce &lt;a href=&#34;https://developer.salesforce.com/docs/atlas.en-us.234.0.dataLoader.meta/dataLoader/data_loader.htm&#34;&gt;Data Loader application&lt;/a&gt; or &lt;a href=&#34;https://dataloader.io/&#34;&gt;DataLoader.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For more info about the tech we used in this project, check out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.algolia.com/&#34;&gt;Algolia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://nuxtjs.org/&#34;&gt;Nuxt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
</feed>
