<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7362939829754027611</id><updated>2026-07-10T03:25:59.532-07:00</updated><category term="CMS"/><category term="Java C++ Wicket"/><category term="media host"/><category term="pagebloom"/><category term="root password"/><title type='text'>Step Ahead Software</title><subtitle type='html'>Keeping you a step ahead!</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-2128807157129041540</id><published>2020-12-18T17:00:00.004-08:00</published><updated>2020-12-18T17:07:07.801-08:00</updated><title type='text'>JDOQL: What is the equivalent of SQL&#39;s count(*) in the JDO world?</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;Displaying large datasets&lt;/h2&gt;There are often times when you need to display data in a table format and the size of the potential dataset is extremely large and so ends up being represented across many pages and displayed with the usual &quot;previous/next&quot; buttons.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Most UI frameworks need to know upfront what is the total count of items you will be displaying so they know how many pages there are and can display some text above the table that says something like:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;font-family: arial;&quot;&gt;Items 31-40 of 397&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;font-family: arial;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Avoiding a big database, memory and CPU hit&lt;/h2&gt;&lt;div&gt;What you don&#39;t want to do is force your JDO ORM (or an ORM using any Java persistence standard e.g. JPA) from instantiating ALL of the objects just so that your UI knows the total count of items to be displayed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To do this you need to perform some kind of &#39;count&#39; query. In JDO this is done by setting the result set to &#39;count&#39; but count needs a parameter.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;e.g.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;PersistenceManager pm = getPm();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;Query q = pm.newQuery(MyClass.class);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // Set up any filtering here ...&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; q.setFilter(&quot;value &amp;gt; 300&quot;);&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;q.setResult(&quot;count(batch)&quot;);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;where batch is an attribute of MyClass, the extent of which, you are querying. Now that&#39;s all well and good but what if you don&#39;t want to perform the &#39;count&#39; on any specific field.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;I just want a total record/object count&lt;/h2&gt;&lt;div&gt;In raw SQL you would do something like:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; select count(*) from mytable;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;but that &#39;*&#39; is an SQL concept only. However, there is an equivalent in JDOQL and given it is OO based you can guess what the equivalent is in JDOQL - &#39;this&#39;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So the following will make sure you get the exact count that you are looking for:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span style=&quot;white-space: pre;&quot;&gt;	&lt;/span&gt;q.setResult(&quot;count(this)&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;If you were to perform a Collection retrieval with the exact same filter then the above count will always match the number of objects in the returned Collection. However, if you use count(&lt;i&gt;attributeName&lt;/i&gt;) this may not always be the case based on uniqueness of the attribute&#39;s value etc.,&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Until we came across the count(&lt;i&gt;this&lt;/i&gt;) construct we were having issues with our counts returning more objects than a query with the exact same filter that returned a collection of the objects. I hope this helps anyone else out there who has this issue.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Extra performance hint&lt;/h2&gt;&lt;div&gt;Some JDBC drivers which will always retrieve all records to perform a &#39;count&#39; operation even if you have specified only a count(this) dataset in JDOQL above.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That is not desirable on large datasets!&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;To overcome this issue with we can, if using the DataNucleus JDO implementation, use its extension that tells it to inform the driver NOT to pull all records to perform a count:&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier; white-space: pre;&quot;&gt;	&lt;/span&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;q.addExtension(&quot;datanucleus.query.resultSizeMethod&quot;, &quot;count&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enjoy!&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/2128807157129041540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/12/jdoql-equivalent-of-count.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2128807157129041540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2128807157129041540'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/12/jdoql-equivalent-of-count.html' title='JDOQL: What is the equivalent of SQL&#39;s count(*) in the JDO world?'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-6129289529274605638</id><published>2020-07-21T12:26:00.002-07:00</published><updated>2020-07-21T12:28:38.878-07:00</updated><title type='text'>Upgrade from MySQL 5.7 to MySQL 8 - Do it! Do it now!</title><content type='html'>Our web hosting application platform Pagebloom has been running on MySQL 5.7 for some time now and it runs brilliantly - it&#39;s exceptionally responsive and provides great user experience.&lt;div&gt;The performance of the underlying MySQL 7 has been great except...&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Clustered indexing: The double edged sword&lt;/h3&gt;&lt;div&gt;Part of MySQL&#39;s great read performance is due to an &quot;under the hood&quot; design feature that most software developers who use it are blissfully unaware of.&lt;/div&gt;&lt;div&gt;In the automobile world it would be the equivalent of fuel injection or a turbo/super charger or in German - a Kompresser! These are features that can be added to the design of an engine to provide a significant boost in performance.&lt;/div&gt;&lt;div&gt;For MySQL this feature is what is known as a &quot;Clustered Index&quot;. The main concept of a clustered index is that the core data of each record is actually stored within the primary key index. Yes, you heard correctly - actual record data is embedded into the B-tree frames that make up the index - I kid you not! At first this may seem crazy but what it means is that for data retrieval that involves locating a record via its primary key - arguably the most common type of retrieval in a relational database - once you have located the correct B-tree frame in the index you have the data right there as well. In non clustered RDBMS architectures only a location reference to the actual data is stored in the index frame so retrieval of the record data at that point requires an extra disk read but in MySQL the retrieval can be done with a single disk read.&lt;/div&gt;&lt;div&gt;&lt;h3&gt;Schema changes are MySQL 5.7&#39;s Achilles heel&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;While a clustered index provides great read performance its write performance has traditionally suffered in the MySQL 5.x versions.&lt;/div&gt;&lt;div&gt;Where the clustered index has caused very noticeable performance issues is when you need to do schema changes that add a column or two to an existing table with millions of records.&lt;/div&gt;&lt;div&gt;If you google &quot;mysql add column slow&quot; you can read endless accounts of slow schema updates with the 5.x series versions. Some people report waiting, literally, for days for it to complete! This can cause real issues with maintenance/upgrades of live production systems.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;MySQL 8 to the rescue!&lt;/h3&gt;&lt;div&gt;For one recent update to Pagebloom the required schema changes were to core tables with literally millions of records that hold organizations, people, users etc., Whilst performing the schema update in a test system we saw the schema update took many hours on MySQL 5.7 - this was unacceptable as, even if we performed the update at 2AM in the morning, we still did not wish to take the live system down for multiple hours. We do most updates at that time of morning in under 60 seconds.&lt;/div&gt;&lt;div&gt;We had heard that MySQL 8 had made some inroads to this &quot;slow schema update&quot; issue and so we thought a possible path was to migrate from MySQL 5.7 to 8.0.21 FIRST and then do the schema update once we&#39;re live on MySQL 8 - it sounded like a plan so we went with it.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;The time to shine is 2AM!&lt;/h3&gt;&lt;div&gt;Early one Monday morning we did the fairly quick migrate from 5.7 to 8 and then the following Tuesday morning it was time for the schema update which had taken many hours in our MySQL 5.7 tests. How long would it take on MySQL 8 in production?&lt;/div&gt;&lt;div&gt;OMG! It completed in under 5 minutes!!! I don&#39;t know exactly what the MySQL team have done with MySQL 8 but it&#39;s like they&#39;ve added a Kompressor/turbo to their clustered index architecture.&lt;/div&gt;&lt;div&gt;This &quot;many orders of magnitude performance improvement&quot; is very rare in the software world but it is very much appreciated and in fact, is basically mandatory for MySQL to be running real, live, production systems in these days of sophisticated database schemas with highly populated tables.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h3&gt;&lt;div&gt;Never be the first to jump in to a new major version release but after it is stable and many guinea pigs have gone before you and suffered all the necessary &quot;burns&quot; required to get a new major release ready for widespread consumption you really should consider upgrading.&lt;/div&gt;&lt;div&gt;In terms of MySQL 8 it is definitely time to migrate from 5.x releases if you have not already done so. The recommended approach is to upgrade to the latest version of the 5.7.x releases first and then the upgrade to 8.0 should be much smoother. Maybe, like us, you will really appreciate the improvements.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/6129289529274605638/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/07/upgrade-from-mysql-57-to-mysql-8-do-it.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/6129289529274605638'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/6129289529274605638'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/07/upgrade-from-mysql-57-to-mysql-8-do-it.html' title='Upgrade from MySQL 5.7 to MySQL 8 - Do it! Do it now!'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-4590024628789671333</id><published>2020-07-16T17:18:00.016-07:00</published><updated>2020-07-16T22:02:41.510-07:00</updated><title type='text'>The case against IP 6: When scarcity can be a good thing</title><content type='html'>With IP 4&#39;s limited range of public IP addresses and free IP 4 addresses becoming scarce it makes sense that the world should migrate over to IP 6 ASAP ... or should it?&lt;br /&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;What&#39;s an IP address anyway?&lt;/h3&gt;&lt;div&gt;A bit of background for those who did not know that the internet predated the world wide web by a good two decades or more:&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;IP addresses are &lt;i&gt;kind of like&lt;/i&gt;&amp;nbsp;a physical street address. You write a physical address of a place anywhere in the world on the front of an envelope, put an appropriate stamp on it and then one or more postal services around the world will cooperate with each other to, hopefully, get your envelope to the &quot;TO&quot; address you specified.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A public IP address identifies a &quot;place&quot; (e.g. a web server) on the internet where internet &quot;data packets&quot; can be sent. There&#39;s a lot of different protocols that can be used to send data across the internet but at the lowest levels they are &lt;i&gt;&lt;b&gt;all &lt;/b&gt;&lt;/i&gt;based on a simple principle: The internet is basically one huge &quot;data packet&quot; delivery network and, just like an envelope, every data packet has a &quot;TO&quot; address embedded in it at a special location in the data packet header and like many envelopes there is also a &quot;FROM&quot; address indicating where this packet is coming from. The job of the &#39;data packet routers&#39; that power the internet is to route data packets along a variety paths to other routers towards their final destination given by the &quot;TO&quot; address.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That&#39;s basically it. I&#39;ve just described the lowest level of the internet in one paragraph.&lt;/div&gt;&lt;div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Why would we run out of IP addresses?&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;In IP version 4, the well established, universal Internet Protocol version that every device on the internet supports today, each IP address that appears as a TO or FROM address in the header of those data packets is stored in just 4 bytes which is essentially a 32 bit long integer.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The maximum number of unique values that can be stored in a 32 bit integer is 2 ^ 32 =&amp;nbsp;4,294,967,296!&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The internet standards declare certain ranges of addresses within this 4,294,967,296 range as &#39;internal&#39; IP addresses - your data packets can&#39;t reach a device on the public internet if you specify at &quot;TO&quot; address which is one of these declared internal IP addresses: every packet routing device on the public internet has been coded so as NOT to pass on any packets with a &quot;TO&quot; address which is within any of the declared &#39;internal&#39; IP address ranges.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So the maximum number of public addresses is reduced by the number of declared &#39;internal&#39; addresses (and some other complex technical issues which we don&#39;t need to visit here) so let&#39;s just say there&#39;s &quot;about&quot; 4 billion public IP addresses all up.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Back in the early days of the internet 4 billion seemed like an incredibly large number but now that the internet has &lt;i&gt;really kicked on &lt;/i&gt;suddenly ... not so much.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Every publicly accessible device on the planet needs a public IP address. This means publicly accessible websites like google, Facebook, your employer&#39;s website, your own website, mail servers etc., all need a public IP address or, in the case of the &quot;big boys&quot;, a range of IP addresses.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;With many billions of devices on the internet why haven&#39;t we broke the internet yet?&lt;/h3&gt;&lt;div&gt;Even though there&#39;s roughly 7 billion people on the planet everyone seems to be having no problem with using the internet with their multiple devices. You can post a message to someone&#39;s Facebook Messenger account half way across the world and their smart phone will instantly &quot;Ping!&quot; them to tell them they&#39;ve received your post. That might appear like their device is accessible to you or, at least, Facebook from anywhere in the world and you might think their device would need a public IP address for this to happen but, fortunately, with 7 billion people on the planet it is &lt;i&gt;&lt;b&gt;not required&lt;/b&gt;&lt;/i&gt; for every person&#39;s device to have its own publicly accessible IP address!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In regards to websites needing a public IP address - they don&#39;t actually need their &lt;i&gt;own unique&lt;/i&gt; IP address - they can share IP addresses. Many web hosting companies will locate many smaller websites at the same public IP address. That used to be an issue when it came to SSL/TLS certificates that allow for the HTTPS instead of HTTP URLs and give you the comforting &#39;Padlock&#39; icon in your browser but for many years now SSL/TLS certificates can work without requiring a unique public IP address for every domain name they secure.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In fact not every worker&#39;s PC in an office building needs their own public IP address. In the very early days many organizations actually used to do this but the world soon realized that this was a very bad idea because the organization&#39;s potential &quot;attack surface&quot; exposed to hackers become orders of magnitude larger than if the organization only had a few publicly accessible devices.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To avoid this dangerous exposure of internal devices via public IP addresses inside an organization we have the now ubiquitous technology known as &#39;masquerading firewalls&#39;: It allows only a few public facing devices to have &quot;exposed&quot; public IP addresses and everyone else sits nice and safely behind a firewall using those &#39;internal&#39; IP addresses we spoke of earlier - to which no one on the public internet (e.g. hackers) can send data packets directly.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While improving security &#39;masquerading&#39; had the side effect of each organization requiring far less public IP addresses than would otherwise be necessary.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;What about home internet connections?&lt;/h3&gt;&lt;div&gt;Up until relative recently in the history of the internet home internet connections were assigned public IP addresses by their ISPs when ever they connected - this consumed a lot of public IP addresses but in recent years new ISP connectivity technology means that ISPs only need to grant home connections &#39;internal&#39; IP addresses. It means that their home modem/router is no longer directly publicly accessible from the internet but, again, that&#39;s an excellent security feature.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For others who might need a public, unchanging (static) IP address most ISPs offer that option for a small extra monthly fee. This is only necessary for people who want to host, for example, a web server or mail server from their home.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Internet advancements lead to less public IPs required&lt;/h3&gt;&lt;div&gt;So what we have is improvements in internet technologies that have both:&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Increased security by shielding end user devices both at home and within organizations by assigning them private IP addresses instead of public IP addresses&lt;/li&gt;&lt;li&gt;Decreased the demand for public IP addresses&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;So what does IP 6 do differently?&lt;/h3&gt;&lt;div&gt;Unfortunately IP 6 does a lot of things differently. If all IP 6 did differently was use, no correlation intended, say 6 bytes instead of IP 4&#39;s 4 bytes for the TO and FROM IP addresses in every internet data packet it would have been much easier for device manufacturers, software developers and operations teams to adopt but noooooo...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Why would the committee who work on such things make such a trivial, small change that gets us a greater range of public IP addresses very simply when they could change &lt;i&gt;&lt;b&gt;*a lot of things*&lt;/b&gt;&lt;/i&gt; and make it so much harder for the world to adopt it?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If IP 6 had 6 bytes instead of IP 4&#39;s 4 byte IP addresses it would have given the world 65 thousand times the 4 billion public IP 4 addresses we have now. 6 bytes is 48 bits so 2^48=281,474,976,710,656. So about 281 trillion!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Having such a plentiful range of public IP addresses would have been sufficient one would think.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But no, clearly bigger is always better so IP 6 went with a 16 byte IP address - that&#39;s a massive 128 bits.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I once heard a professor say that a storage unit that is 128 bits long can store one of 2^128 unique values which gives you enough range to allocate a unique integer to EVERY ATOM IN THE UNIVERSE!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To say the IP 6 committee made sure the universe will never, ever run out of public IP addresses is an understatement. We would actually need more universes to use up that range.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;The law of scarcity: bigger is not always better&lt;/h3&gt;&lt;div&gt;So, clearly, as time went by masquerading technologies intended to increase internet security actually tempered the ever growing need for more IP 4 public IP addresses and so the internet still works, luckily!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So what&#39;s so bad about IP 6 having such a big range of available public IP addresses?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Funny enough, it&#39;s once again about security. You may have heard of White lists/Black lists which are lists of IP addresses that a certain device or software will always ALLOW or DENY access to.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;These are essential in so many areas of today&#39;s internet:&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Logon access denial: too many failed attempts to log on from a particular IP address starts to look like some &#39;bot&#39; attempting a brute force attack on the system. A good system will lock out that IP address by adding it to a permanent or temporary &#39;deny list&#39;.&lt;/li&gt;&lt;li&gt;Website screen scrapers: some &#39;bot&#39; apps scan the content of certain websites to try to extract data that they do not wish to spend time or money obtaining in more honest ways. The website administrator can manually or automatically set up blockers that block access to such actors based purely on their IP addresses.&lt;/li&gt;&lt;li&gt;Website search engine bots: if these are drawing too many resources they can be blocked by IP address.&lt;/li&gt;&lt;li&gt;Email SPAM avoidance. You may have heard of IP reputation in the realm of email SPAM filtering. If too much SPAM is generated from a particular IP address it is assumed to be controlled by a SPAMMER/hacker and so earns a bad reputation by the large network of email monitoring services. Many SPAM filters will use that &#39;reputation&#39; level to determine if you receive email from that IP address in your Inbox, in your SPAM folder or whether it&#39;s sent straight to trash.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;There are many more cases where IP filtering/blocking is used but the important point is IT WORKS BECAUSE IP 4 ADDRESSES ARE SCARCE!&lt;/div&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Scarcity increases value&lt;/h3&gt;&lt;div&gt;IP 4 addresses are scarce and like anything that is scarce, like gold, it has much greater value than something that is abundant - like IP 6 public IP addresses. As world governments print more and more money they reduce it&#39;s scarcity and by doing so, deflate its value.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It&#39;s the old law of supply and demand in practice: Under IP 4 the supply of public IP addresses is very scarce but their demand is high so they become very valuable. It&#39;s hard to get your ISP or IP registrar to just give out even a tiny bunch of IP addresses at a time because they are so scarce.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Not so with IP 6 with it&#39;s 2^128 possible range... IP 6 public IPs are essentially worthless - they are like a crypto currency where miners can generate 1 new coin every millisecond on a PC with a 486 CPU - the coins end up totally worthless.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If hackers or spammers had unlimited access to large ranges of public IP addresses they would simply run their nefarious activities and eventually get their IPs all blocked or denied or have their SPAM reputation eroded but they would not care. Due to the massive amount of available IP 6 public addresses they would simply request another million every week, day or hour as required. They&#39;re virtually infinite, so can&#39;t be too expensive.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Have a thought for all those IP blocking tools I mentioned above. Instead of keeping a database of, possibly, thousands of &#39;naughty&#39; public IP addresses they need to potentially keep a database that&#39;s large enough to store a unique number for every atom in the universe...&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This would create an almost impossible burden on website administrators, operations staff and mail service operators of maintaining the list of all those &#39;bad&#39; IP addresses which could likely hit the millions, billions or possibly&amp;nbsp;trillions!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;However, in the IP 4 world this can&#39;t happen because IP 4 addresses are scarce and very valuable. Organizations don&#39;t just hand them out like there&#39;s no tomorrow. Under IP 4 spammers, hackers etc., find it impossible to acquire large numbers of public IP addresses so their activities can be limited to some extent.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Stay safe. Stay valuable. Stay small.&lt;/h3&gt;&lt;div&gt;Contrary to popular belief&amp;nbsp; bigger is not always better. Smaller is more efficient and, above all, much safer. IP masquerading technologies are protecting more devices and requiring much less public IP addresses than before. The fear of running out of IP 4 IP addresses and the measures taken to overcome that fear, i.e. an infinite range of IP 6 addresses, needs more rational thought and careful consideration of all the consequences and subsequent devaluation that unlimited supply brings.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/4590024628789671333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/07/the-case-against-ip-6-when-scarcity-can.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/4590024628789671333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/4590024628789671333'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/07/the-case-against-ip-6-when-scarcity-can.html' title='The case against IP 6: When scarcity can be a good thing'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-6144690055742836438</id><published>2020-07-09T17:37:00.002-07:00</published><updated>2020-07-09T17:39:58.058-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="root password"/><title type='text'>How to reset MySQL 8 root password on CentOS 7 and 8</title><content type='html'>Occasionally you come up with the world&#39;s most unbreakable root password for a MySQL 8 installation and you get distracted before you record it somewhere.... and you remain distracted for 3 months.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Then you return to the project where this database was needed and ... whoops! What was that totally awesome password?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here&#39;s how you can reset your root password. This was based on a page that had a similar instructions except none of them worked - lots of errors and incorrect names - like referring to mysql instead of mysqld when stopping the service.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;font size=&quot;5&quot;&gt;Here we go:&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All as root user:&lt;br /&gt;&lt;br /&gt;Create a file in /var/lib/mysql called mysql-pwd with the following contents:&lt;br /&gt;&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;ALTER USER &#39;root&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;anotherPwd28^$94&#39;;&lt;/pre&gt;&lt;br /&gt;Shutdown mysql&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;&amp;nbsp; systemctl stop mysqld&lt;/pre&gt;&lt;br /&gt;Run MySQL executing the given init file under the mysql user&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;&amp;nbsp; mysqld --init-file=/var/lib/mysql/mysql-pwd --user=mysql &amp;amp;&lt;/pre&gt;&lt;br /&gt;Check the MySQL log to make sure no errors occurred:&lt;br /&gt;&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;&amp;nbsp; vi /var/log/mysqld.log&lt;/pre&gt;&lt;br /&gt;Stop the mysqld that was started directly by first finding its &lt;i&gt;PROCESSID&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;&amp;nbsp; ps -efa | grep mysqld&lt;/pre&gt;&lt;br /&gt;Stop it:&lt;br /&gt;&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;&amp;nbsp; kill &lt;i&gt;PROCESSID&lt;/i&gt;&lt;/pre&gt;&lt;br /&gt;Start up mysql the normal systemd way:&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;&amp;nbsp; systemctl start mysqld&lt;/pre&gt;&lt;br /&gt;Log in using the password contained in the mysql-pwd file&lt;br /&gt;&lt;pre style=&quot;text-align: left;&quot;&gt;&amp;nbsp; mysql -u root -p&lt;/pre&gt;&lt;br /&gt;You should now be logged in as root user&lt;br /&gt;&lt;br /&gt;Edit the mysql-pwd file to remove the password value so that root password is not available to people viewing that file.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/6144690055742836438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/07/how-to-reset-mysql-8-root-password-on.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/6144690055742836438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/6144690055742836438'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2020/07/how-to-reset-mysql-8-root-password-on.html' title='How to reset MySQL 8 root password on CentOS 7 and 8'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-2943994146124278869</id><published>2019-10-29T13:50:00.001-07:00</published><updated>2019-10-29T13:50:20.479-07:00</updated><title type='text'>Asynchronous notifications from the server to the browser - perfect</title><content type='html'>This is why I love Wicket Java Web UI framework.&lt;br /&gt;
&lt;br /&gt;
It provides all the power of the latest JavaScript and browser capabilities without ever having to write any code in that crazy, non type safe, interpreted JavaScript language ;)&lt;br /&gt;
&lt;br /&gt;
Wicket components have all of the required JavaScript embedded within them meaning a Java developer of any skill level can simply instantiate a few components, add them to a page and bang! - too easy.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
So about asynchronous notifications...&lt;/h2&gt;
&lt;h3&gt;
The &#39;status update&#39; problem&lt;/h3&gt;
&lt;div&gt;
Often within a web app the user will invoke some operation that can take some time. The page or part of it needs to be updated to reflect the new status after the operation has completed.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To do this developers, traditionally, have had to do one of these:&lt;/div&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Make a request to the server which the server delays responding to until the operation has completed. This is inviting bad karma to your web server because it ties up a connection and the number of connections in a web/app server are not infinite. What&#39;s worse, in many application servers tying up a connection also ties up a thread - which consumes much more resources than merely tying up a connection.&lt;/li&gt;
&lt;li&gt;Start up a poller in JavaScript which regularly polls the server to get the status on the operation. When the operation is complete the poller is disabled and the relevant panel(s) on the page are updated. This option has a latency issue: for example if you set the polling period to 2 seconds then the user might have to wait for up to 2 seconds after the operation has completed before they see the update. You can make the polling period very small but this solution will not scale well as with thousands of users all hammering the server with update requests may place too much burden on the server.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
A better solution&lt;/h3&gt;
&lt;div&gt;
Quite a long time ago a technology called &quot;Web sockets&quot; was introduced which allows for the server to asynchronously notify a client (browser) of a change of state. e.g. the completion of an operation or an update of the value of a stock.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Wicket provides a very simple &quot;wrapper&quot; for Web sockets which allows the developer to add Web socket support to their app without writing a single line of JavaScript code - it can all be done in Java - the clever Wicket devs have done all the hard JavaScript coding yards for us, once again! (Thank you guys!)&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Here is an example of an asynchronously updated graph:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://examples8x.wicket.apache.org/websockets/behavior&quot;&gt;http://examples8x.wicket.apache.org/websockets/behavior&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Here is a tutorial on how to write an Web sockets app using Wicket:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://dzone.com/articles/websockets-with-wicket-tutorial&quot;&gt;https://dzone.com/articles/websockets-with-wicket-tutorial&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/2943994146124278869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2019/10/asynchronous-notifications-from-server.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2943994146124278869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2943994146124278869'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2019/10/asynchronous-notifications-from-server.html' title='Asynchronous notifications from the server to the browser - perfect'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-8162602083460713543</id><published>2019-10-21T19:19:00.000-07:00</published><updated>2019-10-21T19:19:52.579-07:00</updated><title type='text'>I&#39;m coming clean. I&#39;m letting everyone know that I&#39;m a bug addict...</title><content type='html'>The other day I added a new, rather complex feature to our &lt;a href=&quot;http://filegroove.com/&quot;&gt;Filegroove&lt;/a&gt; product. I did the normal planning and then analyzed the pros and cons of a few different design implementations.&lt;br /&gt;
&lt;br /&gt;
I then went ahead and implemented the chosen design and ...&lt;br /&gt;
... it was a bit of an anticlimax when it basically worked as designed, first go 🙂&lt;br /&gt;
&lt;br /&gt;
Normally I have the excitement of hunting down some tricky bug that needs super detective skills because of some incorrect assumption I had made or due to some oversight but no ... the damn thing just worked!&lt;br /&gt;
&lt;br /&gt;
My reaction made me ponder:&lt;br /&gt;
&lt;br /&gt;
I think I&#39;ve become addicted to the dopamine hit I get when I solve a tricky bug. I miss that when everything just works - need to get my dopamine hit from somewhere else - maybe some FB pings? LoL!&lt;br /&gt;
&lt;br /&gt;
Here&#39;s my official admission:&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;trebuchet ms&amp;quot; , sans-serif; font-size: x-large;&quot;&gt;I&#39;m a bug addict. I&#39;ve been using bugs for many year now.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;trebuchet ms&amp;quot; , sans-serif; font-size: x-large;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/i&gt;
&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;trebuchet ms&amp;quot; , sans-serif; font-size: x-large;&quot;&gt;I tried to get off the bugs but its just too hard.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;trebuchet ms&amp;quot; , sans-serif; font-size: x-large;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/i&gt;
&lt;i&gt;&lt;span style=&quot;font-family: &amp;quot;trebuchet ms&amp;quot; , sans-serif; font-size: x-large;&quot;&gt;After a few days without bugs I can&#39;t sleep at night and go looking for a fix - boom tish!!!!!!&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Maybe that&#39;s why I called myself the OO Junky? :)</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/8162602083460713543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2019/10/im-coming-clean-im-letting-everyone.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/8162602083460713543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/8162602083460713543'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2019/10/im-coming-clean-im-letting-everyone.html' title='I&#39;m coming clean. I&#39;m letting everyone know that I&#39;m a bug addict...'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-3310237157157714235</id><published>2012-10-22T15:16:00.002-07:00</published><updated>2012-10-22T15:17:31.993-07:00</updated><title type='text'>Git and Subversion - the working directory is fundamentally different</title><content type='html'>One of the fundamental differences between Git and Subversion that I didn&#39;t understand at first and, IMHO, doesn&#39;t really get much coverage in the documentation is the way the &#39;working directory&#39; is treated.&lt;br /&gt;
&lt;br /&gt;
Git is all about branching and it is indeed very good at that but what happens when you switch branches in Git is completely different to when you switch branches in Subversion.&lt;br /&gt;
&lt;br /&gt;
You could say that you never really &quot;switch&quot; branches in Subversion. You can create branches and checkout a branch in subversion: In subversion if you want to use a branch you typically &#39;check it out&#39; to a new clean &#39;working directory&#39;. This directory is a new place in your filesystem and you perform work on that branch in this new working directory. You can&#39;t easily &#39;switch&#39; a working directory from one branch to another branch.&lt;br /&gt;
&lt;br /&gt;
With subversion if you are working on a number of different branches then you would typically have a number of different &#39;working directories&#39;, each being for a specific branch. In Git things are very different...&lt;br /&gt;
&lt;br /&gt;
In Git you typically have a single &#39;working directory&#39; and when you want to work on a different branch to the current one you literally &#39;switch&#39; branches and the source files in your working directory are modified to reflect the state of the files in that different branch. When you switch to another branch the source files are updated again to reflect the current state of every file in that other branch.&lt;br /&gt;
&lt;br /&gt;
At first this seems like a lot of work for the revision control system to be performing but git seems to handle it in its stride - and very quickly. Most of the time the differences between two branches are only relatively small compared to the size of the overall code base. Git is very fast at storing &#39;deltas&#39; and rendering and backing out those deltas on source files as required by branch switching.&lt;br /&gt;
&lt;br /&gt;
You might ask, &quot;what about changes that I have made to files in the current branch?&quot;&lt;br /&gt;
&lt;br /&gt;
Git won&#39;t let you switch branches if you have changed files in your working directory. You have to either commit the changes (not so bad as you&#39;re only committing to your local repository) or &#39;stash&#39; the changes. Stashing tells git to store all the changes you&#39;ve made in the current branch without a more official and long term &#39;commit&#39;. You could say &#39;stash&#39; is the perfect solution for developers who are scared of commitment. You can leave your current branch after a stash and return to it whenever you feel like it.&lt;br /&gt;
&lt;br /&gt;
So the ability to switch between branches in Git means that you can have very quick access to any branch in your repository without having to checkout a new, full working directory on the filesystem. In other words, Git branch &#39;switching&#39; is a lot faster than Subversion&#39;s &#39;branch&#39; checkout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I have to admit I wasn&#39;t at all excited by the arrival of Git at first - &quot;Oh no! Another revision control tool to master&quot; but many of the open source projects that I use on a regular basis are now hosted on GitHub so I have undergone a forced introduction to Git. I have to say that the more I use it the more I like it!</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/3310237157157714235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/10/git-fundamental-difference-to-subversion.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/3310237157157714235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/3310237157157714235'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/10/git-fundamental-difference-to-subversion.html' title='Git and Subversion - the working directory is fundamentally different'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-5628786233945316598</id><published>2012-09-10T23:50:00.002-07:00</published><updated>2019-02-11T17:05:26.363-08:00</updated><title type='text'>Classic software engineering mistakes: To Greenfield or Refactor Legacy code?</title><content type='html'>&lt;h2&gt;
Oh, the humanity!&lt;/h2&gt;
&lt;br /&gt;
If we were not in the software engineering game but, rather, in the civil engineering game then the equivalent of this article would be called &#39;Classic Civil Engineering Mistakes&#39; and contain graphic videos of buildings and bridges collapsing and thousands of people running for their lives.&lt;br /&gt;
&lt;h2&gt;
It&#39;s hard to get emotional about something you can&#39;t see&lt;/h2&gt;
Unfortunately software is a rather intangible asset that doesn&#39;t lend itself to dramatic and emotional visualizations. When it comes down to it the life work of a software engineer, when reduced to it&#39;s lowest conceptual level, is the specific arrangement of sequences of 1&#39;s and 0&#39;s on one or more hard disks (or some form of solid state storage) residing on a server or desktop computer. How those 1&#39;s and 0&#39;s get there and &#39;which&#39; sequences work and which don&#39;t is not really a black art but it definitely requires some intelligence but surprisingly a lot more common sense.&lt;br /&gt;
&lt;br /&gt;
Some arrangements of 1&#39;s and 0&#39;s form social networking services like Facebook and Twitter, earning their creators billions of dollars. Other arrangements are from lesser known companies that, while not making billionaires out of company owners and millions for employees, can provide sufficient income to keep families fed and mortgages paid.&lt;br /&gt;
&lt;br /&gt;
Other arrangements of 1&#39;s and 0&#39;s never earn any money at all. Still others begin successfully for their creators but through circumstance, changing environment or poor decisions the success teeters off either abruptly or over an extended period.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Opportunity knocks &lt;/h2&gt;
At certain times in a software company&#39;s life new market opportunities, constraints or internal political rumblings arise in relation to one or more products produced by the company that demand analysis of various possible strategies going forward. Often the intended end result is an extension or enhancement or refinement of an existing product. The evaluation of a way forward usually involves choosing between one of the following basic strategies:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Creating a new greenfield code base for either all functionality or a portion of it.&lt;/li&gt;
&lt;li&gt;Retain a single code base for legacy and new products, refactoring, enhancing, refining the legacy code base where required to provide the extra functionality required by the new product. The same code base can execute in multiple &#39;contexts&#39; (i.e. products) via well established context dependent coding mechanisms (eg., standard factory patterns, template pattern etc.,) where certain behaviors are abstracted and context/product specific implementations are &#39;plugged in&#39; at run time.&lt;/li&gt;
&lt;li&gt;Copy/paste the legacy code base, creating a completely independent code base for the new product such that there is no requirement to support the legacy system in the duplicated code base - support for the legacy system will be provided via the original legacy code base.&lt;/li&gt;
&lt;li&gt;A hybrid of 1 plus either 2 or 3 where new functionality can be sufficiently decoupled that it can be developed as greenfield project but it integrates with the legacy system (2) or a copy of it (3) through well defined interfaces - some of which may not exist yet but can be added, usually with some fairly straightforward refactoring and abstraction of certain functionality within the legacy system.&lt;/li&gt;
&lt;/ol&gt;
This article relates my experience during an early software engineering position I held 20+ years ago when I was bright eyed and bushy tailed and very naive (I like to believe that I&#39;m still bright eyed and bushy tailed but no longer so naive!)&lt;br /&gt;
&lt;br /&gt;
Some seemingly plausible but, in the end, damaging strategic decisions were made in regard to the way the software team arranged their 1&#39;s and 0&#39;s as you&#39;ll see...&lt;br /&gt;
&lt;h2&gt;
The Company &lt;/h2&gt;
The company I worked for produced software to run in sophisticated controllers for at least three different products. Let’s just call them  ‘engines’ so as to not make the company identifiable. They all supported very similar functionality but varied in the hardware that they were deployed to so the software for the three different engines was built from the same code base – in places where engine specific code was required we used C’s pre-processor macros to customize the code for the different engines (this was in the infant days of OO and not many people had heard about C++ - which would have been a more elegant solution)&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;#ifdef ENGINE1&lt;br /&gt; …&lt;br /&gt; #elif ENGINE2 &lt;br /&gt;… &lt;br /&gt;#else // All other engines&lt;br /&gt;...&lt;/span&gt;  &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;br /&gt;#endif &lt;/span&gt;  &lt;br /&gt;
&lt;h4&gt;
Example of an engine specific section of code&lt;/h4&gt;
99% of the code didn’t need this as it was common to all products but some of the source files had it and while inelegant, it meant we only had to maintain a single code base. Any bugs or features added to the common code for one product was automatically inherited, at no extra cost or effort, by the other products. It was very maintainable, highly productive and made a lot of sense.&lt;br /&gt;
&lt;br /&gt;
but... one day a (insert adjective that starts with &#39;n&#39;) software team leader decided it would be much &#39;cleaner&#39; if we separated the code into three separate code bases and remove all the pre-processor macros. So without any further discussion and much to the amazement of some of the more astute software engineers, he split the code into 3 separate code bases even though 99% of it was common to all engines.&lt;br /&gt;
&lt;br /&gt;
The theory was that by removing the ‘ugly’ engine specifics from the code we’d all be working on ‘cleaner’ code and therefore productivity would go through the roof. &lt;br /&gt;
&lt;br /&gt;
That was the beginning of the end for the software department in a historically successful, medium sized enterprise. I won’t mention the name because I don’t want to offend anyone who was involved in the decisions that pre-empted the escalation in costs that contributed to the eventual closure of its software department.&lt;br /&gt;
&lt;br /&gt;
[Aside: very few of the pre-processor directives were ever removed from 
the code bases so the perceived &#39;ugliness&#39; was never removed - we just had 3 
separate versions of it :) ]&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Three separate code bases: What could possibly go wrong?&lt;/h2&gt;
&lt;br /&gt;
In the beginning the three code bases were still fairly identical so deltas and merges were almost manageable but before too long subtle differences meant that simple copy/pastes from one source base to another was almost guaranteed to cause regression bugs. In sections of the code that were previously purely generic (i.e. no engine specifics) bugs were being raised by customers for one product that were not present in the other. It quickly became a dog’s breakfast that was very expensive to maintain. &lt;br /&gt;
&lt;br /&gt;
Within months a hiring spree ensued because now when a bug was found it had to be fixed in three places instead of one and when new features were added it had to be added to three files instead of one - the team needed more developers to perform this laborious &#39;duplication of effort&#39;. As a developer I recall suffering the classic &#39;same but different&#39; problem that occurs when you work in different instances of essentially the same thing - there is very little contextual difference as things look so similar so, a few weeks later, remembering which &#39;instances&#39; you fixed and which one you didn&#39;t becomes very challenging.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
&quot;Same but different&quot; can be dangerous to your health &lt;/h3&gt;
There was once a German car racing track that relied on this &#39;same but different&#39; confusion to trick drivers. It had multiple bends that were deliberately designed to look &#39;exactly the same&#39; and they &lt;i&gt;were&lt;/i&gt; essentially the same except for the last one, the exit of which went in a completely different direction to the rest. You can guess what happened - they had to close the track from too many driver deaths as many drivers turned the wrong way on exiting from the last bend - confusing it with another that went the opposite way. Due to the confusion caused by the &#39;same but different&#39; phenomenon their brains lost track of which instance of the &#39;same thing&#39; they were driving in.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Anyway, enough about real things like cars and racing tracks and back to 1&#39;s and 0&#39;s...&lt;br /&gt;
&lt;br /&gt;
Many more developer hours were required to maintain three code bases than the previous single code base – which makes perfect sense to the average man in the street even though the people initially pushing for the ‘cleaner’ code base(s) found this outcome *surprising*. Nevertheless they arrogantly continued with this &#39;new&#39; approach to how we arranged our 1&#39;s and 0&#39;s because to go back would mean an admission that they were wrong and for &#39;n&#39; type people that is very hard to do. One of the guys leading the team was one of those personality types that would rather run the ship into an iceberg rather than admit they had the ship pointing in the wrong direction.&lt;br /&gt;
&lt;br /&gt;
Pretty soon the quality of all three code bases dropped dramatically as the maintenance effort was overwhelming even the larger team that had been established. The products with the least market share started being treated like second rate citizens with all effort going into the most popular products. Previously, updates in the common code (99% of it) were occurring for the less popular products for free, now they weren’t happening very much at all as it became too expensive and time consuming to port new features and bug fixes across from one code base to another. &lt;br /&gt;
&lt;br /&gt;
The company had effectively close to tripled the cost of maintaining and enhancing its cash cow’s software code base without a single extra dollar in revenue coming in for all that extra effort and cost. Customer&#39;s don&#39;t really care if you&#39;ve got pre-processor macros to implement conditional builds in your code so long as the code works.&lt;br /&gt;
&lt;br /&gt;
Rather than realizing the expected, yet completely unquantified, productivity gains from establishing multiple, but ‘cleaner’, code bases the productivity dropped dramatically as did the quality of released products. The sales team were constantly asking “Why is bug X fixed in product Y but is still there in product Z? I promised the customers it would be fixed in the latest release and it&#39;s not. You have embarrassed me.” and customer satisfaction dropped accordingly. &lt;br /&gt;
&lt;br /&gt;
This plane’s altitude was dropping quickly but it still had two of the four engines running and could have limped to the nearest airport if they were smart about it but only the bravest and humblest manager would be able to make the smart and responsible decision required at this stage in the game…evidently that type of person didn&#39;t make it to the second round interviews at this place ;)&lt;br /&gt;
&lt;h2&gt;
New &#39;clean&#39; greenfield project: What could possibly go wrong?&lt;/h2&gt;
&lt;br /&gt;
What happened next was a very exciting opportunity offered to many of the software engineers and therefore well accepted by all the software team (including me!) – a new greenfield project called the ‘re-engineering project’ (I’m sure thousands of companies have had projects with re-engineering in their title so I’m not giving away any company identification hints here) which, in recognition of the set of 3 separate unmanageable code bases was to rebuild a new “re-engineered’’ single code base from which the software for all products could be produced. Common, reusable frameworks and libaries would be built. It was a very ‘back to the future’ experience – creating a new single codebase – much like we had before the code split except that all the code would be shiny and new and therefore much, much better!&lt;br /&gt;
&lt;br /&gt;
The ‘re-engineering project’ started with great enthusiasm and it was decided early on to avoid using any of that yucky, dirty existing code that had undergone years of development and field proven robustness.&lt;br /&gt;
&lt;br /&gt;
Initially about 18 engineers, including myself were put on the project. It would use the latest &#39;state of the art&#39; CASE tools whose licensing cost thousands of dollars per engineer - not to mention the cost of training. Everyone was very positive and keen to &quot;get into it&quot;. What could possibly go wrong?&lt;br /&gt;
&lt;br /&gt;
Well there was one minor problem - the project would take 12-18 months and would not earn 1 cent in revenue until it had been completed, fully tested and customers started buying it. Meanwhile there was a &quot;dog’s breakfast&quot; of 3 difference code bases that needed to be maintained and the ‘re-engineering project’ just stole about 18 engineers from the maintenance effort. Whoops!&lt;br /&gt;
&lt;br /&gt;
As you may have guessed, in a company with limited financial resources, engineers started getting poached from the re-engineering project and put back on maintaining the old code for the cash cow products. When we were down to about 6 engineers I remember thinking, “Ok, this project’s ETA has just been extended by about another 24 months”. Unfortunately that wasn’t the end of the poaching.&lt;br /&gt;
&lt;br /&gt;
The re-engineering earned ZERO dollars and was of intangible benefit in management’s eyes. Maintenance and quality of the existing products, which WERE EARNING MONEY was dropping as less engineers were available to maintain them. Over the next six months management gave directions to R&amp;amp;D to poach even more engineers back to maintenance to improve quality and turn around times for new features. This continued until there was only a single person left on the re-engineering project…me. While rather chuffed to be the last one standing it was pretty damn lonely and the ETA had now become a company joke: 18 engineers = 18 months so 1 engineer = 324 months! &lt;br /&gt;
&lt;br /&gt;
So what can software managers learn from these types of experiences to make better decisions for how their team organize their 1&#39;s and 0&#39;s? Well it depends on a number of variables. What may be surprising to some is that a very important variable is a non technical one: the size and therefore financial resources of the company. Here&#39;s a list of typical outcomes based on company size:&lt;br /&gt;
&lt;h2&gt;
The little guys&lt;/h2&gt;
The little guys, small enterprises, rarely have a choice. Their decision is constrained by the economic reality of very limited financial resources and they usually don&#39;t have the financial resources for a full greenfield &#39;rewrite&#39; of their code base. Often their only option is to &#39;get agile&#39;: continually improve, refactor and enhance their existing code base. Over time much ugliness in the code can be replaced by elegant design. The improvements are incremental over time instead of &#39;big bang&#39; at the end of a 2 year project development - which a small company simply can&#39;t afford.&lt;br /&gt;
&lt;br /&gt;
As turn around times for each iteration is usually from 1-4 weeks the improvements to the existing code base are regular and financial benefits can be realized in the very short term. In other words, agile style, incremental improvement of the legacy code base produces a repetitive, financially viable sequence of deliverables with excellent &#39;return on investment&#39; (ROI).&lt;br /&gt;
&lt;br /&gt;
In my experience this is usually successful as it does not incur large financial commitments on behalf of the small enterprise. My second place of employment was a small but successful company. I wrote a sophisticated Windows application for them in the early 90s. It was ported from 16 to 32 bit when Windows 95 was released and they are still deploying it 20 years later and have continued enhancing and maintaining it over two decades.&lt;br /&gt;
&lt;br /&gt;
My own company is relatively small yet we have developed many reusable frameworks over many years and we have incrementally enhanced and refactored these on a continuing basis. We have never hit any requirement that has caused us to even come close to being tempted to &#39;ditch it and start with a clean slate&#39;. It certainly helps to design with flexibility built in - often abstracting behaviors into clearly defined interfaces to reduce coupling and increase re-usability - but even with an old code base that wasn&#39;t built with this is mind it is usually possible to refactor to repackage legacy code into an implementation of an interface that then allows that behavior to be reused by other modules or even expose that behavior as a service to be reused by other applications. It also allows other modules or applications to provide their own implementation of that interface. The trick for knowing when an opportunity for such abstraction exists is to be ever vigilant against the temptation to copy/paste. If you ever feel the need to copy/paste a section of code then right there you probably have a candidate for some abstraction and re-usability.&lt;br /&gt;
&lt;h2&gt;
The big guys&lt;/h2&gt;
The big guys, large enterprises typically do succeed with large, &#39;big bang&#39;, greenfield projects. If you’re a Microsoft or Google you 
typically have enough resources that you can do all the extra hiring you
 need and don’t need to pull staff off one project to start another and 
so there’s no need to poach people from the Greenfield/Re-engineering 
project and put them back on maintenance of the company&#39;s existing cash cow product(s) when the legacy code suffers from neglect. This approach can work if you happen to be a Fortune 50 company
 with infinite funds to invest in product development and so don’t need 
to poach resources from the Greenfield project back to maintenance 
roles.&lt;br /&gt;
&lt;h2&gt;
The medium sized guys &lt;/h2&gt;
Stuck between little guys and big guys is the medium sized enterprises. This is where things get very tricky. They are not like the little guys whose decision is forced upon them due to &#39;shoe string&#39; budgets but equally they are not like the big guys whose seemingly infinite budgets mean they can fund even the largest of projects.&lt;br /&gt;
&lt;br /&gt;
These guys often do have budgets that can run into multiple millions but lets be clear: millions are not billions like the big guys deal with. Millions can sound like a lot of money but if an average software engineer is paid around $100k/year and you create a new project that needs 8 engineers you&#39;ve just incurred an extra ~$1,000,000 of costs every year once you include rental space, furniture, benefits, pension contributions, training etc., Has revenue increased by that same amount over that year? Is it expected to increase once new customers use products built with the &#39;new and shiny&#39; code base or won&#39;t most customers notice the difference?&lt;br /&gt;
&lt;br /&gt;
Some managers justify the greenfield approach by saying we&#39;re going to have no new hires or only one or two and so the overall cost to the company is not significant. If you need 8 engineers but hire only two extra engineers then you have to pull 6 engineers from an existing product, typically the company cash cow and that has all the risks I outlined above. Poaching is the main risk - every poaching pushes out the ETA of the greenfield project and reduces it&#39;s chance of success as the ETA approches the &#39;Greenfield Window&#39;.&lt;br /&gt;
&lt;h2&gt;
&lt;/h2&gt;
&lt;h2&gt;
Greenfield Window&lt;/h2&gt;
I use the term &quot;Greenfield Window&quot; to describe the very limited window in which a greenfield project has to succeed. One that window closes it&#39;s game over for the Greenfield project. All the code created is archived in the version control system and rarely ever touched again.&lt;br /&gt;
&lt;br /&gt;
Greenfield projects take lots of developer and financial resources and they don&#39;t earn the company ANY revenue until the product is being used by paying customers. For even modest projects this can take 12 months but in larger products this can take many years.&lt;br /&gt;
&lt;br /&gt;
During greenfield development there are all sorts of pressures placed on the project. Pressures from accountants, management, creditors and shareholders who ask questions like &quot;how much is this costing us and how much has it earned us?&quot; Until the project is completed and the first customer pays you something the answers to this question are always &quot;A LOT &amp;amp; ZERO&quot; which really tests the patience of these stakeholders. It only takes one of these groups to lose patience and the &#39;Greenfield Window&#39; slams shut very abruptly - on your fingers because you were gazing out the window at the time, looking at the greenfield and wondering when your &#39;crop&#39; would be ready for harvest.&lt;br /&gt;
&lt;br /&gt;
This is a very painful experience both financially and emotionally. In addition to thousands or millions of dollars wasted egos are hurt and egg lands firmly on faces. Morale among the team can be affected and office politics and power-plays take on a new vigor as the dust starts to settle under the mushroom cloud. It&#39;s the injuries sustained by egos and subsequent political game playing that can have long lasting effects.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;/h2&gt;
&lt;h2&gt;
Greenfield Specification Vacuum&lt;/h2&gt;
Often legacy code has evolved over such a long time and in a relatively undisciplined way that much of the functionality is not documented anywhere. The code has become the specification. Hundreds of boundary cases and error conditions have been &#39;handled&#39; by the code without any documentation of such behavior. Process/workflows are &#39;embedded&#39; deep into hard coded state machines that make decisions based on a set of business rules that are perhaps known only to the original developer or a business analyst who has long since left and often for situations that arose in real life scenarios that only they were aware of.&lt;br /&gt;
&lt;br /&gt;
Throwing out legacy code and starting with a clean slate often throws the proverbial &lt;i&gt;baby out with the bathwater.&lt;/i&gt; &lt;br /&gt;
&lt;br /&gt;
Most greenfield projects that are designed to replace a legacy code base have no accurate functional specification to work with. Even if one were to be produced it&#39;s coverage of business rules, boundary conditions and exceptions that were implicitly, explicitly or even accidentally built into the legacy code would be severely lacking. A thorough examination of vast quantity of code would be required to establish a functional specification with sufficient coverage before the greenfield project can even commence. This is another advantage that refactoring the legacy base has over a greenfield development: the legacy code contains years of valuable explicit, implicit and accidental functional implementations and, even though they may be subtle, undocumented and hard to find, they are &lt;i&gt;working&lt;/i&gt; and you can reuse them as they are because you don&#39;t have to throw them away and reimplement them.&lt;br /&gt;
&lt;br /&gt;
With incremental improvement/refactoring you can attack these a section at a time over an extended time with each iteration producing better code that customers can start using after the usual QA process has completed. With a greenfield approach you will need to discover, document then reimplement in the new code every undocumented, hidden, subtle business rule and specification. I believe that the calculation of effort involved in this task will grossly underestimated by most software engineers overwhelmed with excitement at the prospect of starting work on a greenfield project.&lt;br /&gt;
&lt;br /&gt;
In fact, if the engineer believes that their estimation of effort to create a functional specification via thorough examination of legacy source code is so large that it will make the project too expensive and therefore jeopardize it&#39;s chance of commencement there may be a temptation to deliberately underestimate the effort and thus create a &#39;surprising&#39; cost blowout down the track. I most carefully used the word &quot;tempation&quot; here:&amp;nbsp; whether an individual succumbs to this temptation or not will depend on their own moral code.&lt;br /&gt;
&lt;br /&gt;
With the nature of typical full time employment arrangements there is rarely any direct financial bonus or penalty associated with accurate or otherwise estimation and/or implementation of functionality within the estimated range. The company takes all the risk for decisions made based on estimates provided by people who have no direct exposure to financial consequences related to the accuracy of such estimates.&lt;br /&gt;
&lt;h2&gt;
Greenfield Partial Functionality&lt;/h2&gt;
One of the &quot;promises&quot; (given without any justification or quantization) raised by promoters of greenfield projects is that the greenfield system will be &quot;all singing and dancing&quot;, &quot;infinitely cool and good&quot; and of course be able to replace the existing system but, like politicians, software engineers are great at making promises but not always able to follow through - especially full time employees for which there is usually no direct financial benefit or penalty for delivering on their promises.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s say that the software team, in the face of overwhelming odds against it, manage to somehow create a working new greenfield product that does something like the old system did. Let&#39;s just say that the first deployment of the new system was for a slightly different market that only needed a subset of the functionality in the legacy system. What you have now is &#39;greenfield partial functionality&#39;.&lt;br /&gt;
&lt;br /&gt;
The greenfield project has created a new source code base that needs a team of engineers to support it but guess what - it only represents a subset of the functionality so that it can&#39;t replace the legacy system. In supporting only a subset of the functionality it is hard to find new customers to use the new software. The legacy system and the new system both need continued development and maintenance. The yearly costs to the company of the software department have now doubled and because the greenfield project has only partially implemented the features of the old system there is no medium term reduction in developer costs. The company now has two different software code bases to maintain for the long term.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s be really generous and say that the new project does implement every feature of the legacy system. We&#39;re still not in software nirvana because the new system, while it may have been based on the original class model or database tables has had quite a number of new attributes/columns added or undergone refactors to improve problems experienced with the old model/database. In other words that 300GB database that represents your customer&#39;s data can&#39;t be easily migrated to work with the new software.&lt;br /&gt;
&lt;br /&gt;
It&#39;s like a 20 ton anchor that isn&#39;t budging until someone can write the &quot;migration script from hell&quot; to bring the old data into the new system. On the contrary, if the legacy system had been incrementally refactored then at each iteration, the data migration (if required) would likely incur very small model/database changes and so very small, manageable data migration scripts are required to migrate the existing data and hence all the users associated with that data.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Greenfield Paradox&lt;/h2&gt;
What I have discussed here is the &#39;greenfield paradox&#39;. It&#39;s a paradox because intuitively, nice clean, shiny things are much nicer to deal with and therefore easier to look after and maintain than old, dirty, ugly things. That might be true of cars, boats and houses but it&#39;s not true of software. Software doesn&#39;t rust or corode. Unlike plastic and wooden decking it doesn&#39;t break down in the sun over time.&lt;br /&gt;
&lt;br /&gt;
Software is not like the &#39;normal&#39; things we touch and feel in everyday life. It can be almost &#39;immortal&#39; unless the environment in which it exists becomes extinct (Good bye DEC VAX we knew you well!). Software written to run in some banks or insurance environments 40 years ago can still run exactly as it ran back then - and some still does! The 1&#39;s and 0&#39;s don&#39;t corode or rust with time (ok, maybe metal &#39;core&#39; based memory from the 60&#39;s might =]). Software is infinitely mutable. It can be bent, reshaped, refactored, re-engineered. It can be changed a little at a time and therefore it can be incrementally improved over time whilst maintaining the current user base and all of their data.&lt;br /&gt;
&lt;br /&gt;
Sometimes there are paradigm shifts in the IT world which force a new greenfield development - for example moving an application from desktop based to cloud based - but if the legacy software was written with a decent model then even though much of the user interface code will need to be rewritten most of the &#39;core&#39; of the application can be reused so long as it was written in a language that has wide adoption (eg., Java, C++ etc.,).&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Hybrid Strategy&lt;/h2&gt;
In some scenarios the extra functionality is sufficiently independent that only the new functionality needs to be  developed as a greenfield project. A significant portion of the functionality can still be provided by the legacy code (strategy 2, preferrable) or a copy of it (strategy 3, duplication of maintenance effort and cost). The integration between the greenfield component and the legacy software can be via intelligently crafted services/interfaces that are provided by the legacy code base. While the hybrid strategy is not the pure &#39;clean slate&#39; approach that many engineers might prefer it can result in massive savings and much faster time to market compared to a complete greenfield rebuild.&lt;br /&gt;
&lt;br /&gt;
The time and effort involved in refactoring certain parts of the legacy code to abstract behaviors and expose them as services via generic interfaces is usually insignificant compared with the time and effort required to create a complete greenfield implementation of the legacy code. If a hybrid strategy of 1 + 2, above, is chosen over hybrid strategy 1 + 3, it means that the company&#39;s code base is not duplicated which avoids a doubling of the company&#39;s maintenance effort and cost.&lt;br /&gt;
&lt;br /&gt;
Refactoring existing functionality so that it can be exposed via a clean interface requires patience, intelligent thought, design and high coverage testing. Not every software engineer has the necessary skill set and, just as important, personality and temperament to be up to this task which is why many will push for a &#39;complete greenfield&#39; strategy. However, if you have one or two engineers who have the right technical skills and personal skills required to implement the intelligent refactoring required to implement a highly efficient hybrid strategy it could represent savings of thousands or even millions of dollars and massively speed up the time to market of your new product. As an added bonus: any improvements made to your legacy code base are instantly available to your legacy products with no further coding effort/expense.&lt;br /&gt;
&lt;h2&gt;
Let the software engineers decide?&lt;/h2&gt;
If you ask 100 software developers whether they would rather work on legacy code written by someone else or create brand new code from scratch then 100/100 software engineers would say &#39;brand new code, of course!&#39;.&lt;br /&gt;
&lt;br /&gt;
My 20+ year’s of industry observation tells me that it takes a very brave and very smart software manager to avoid the mistake of undervaluing the legacy code base. While they can see the potential for a major cost blowout in the construction of a new &#39;bright and shiny thing&#39; they often find it hard to resist the overwhelming pressure from software engineers and others in the organization to &#39;restart with a clean slate&#39; and the promises of &#39;future productivity improvements&#39; that are &#39;guaranteed&#39; by said software engineers. The guarantees are never in writing and the gains are never quantified.&lt;br /&gt;
&lt;br /&gt;
Software engineers hate nothing more than OPC (Other People&#39;s Code). A greenfield project or a &#39;cut and paste&#39; of the legacy code base with no requirements for backwards compatibility represents an extremely tempting though expensive opportunity for them to create an extra code base that software engineers that come after them can have the pleasure of hating for years to come :)&lt;br /&gt;
&lt;br /&gt;
If ensuring popularity with or appeasing the software engineers is a manager&#39;s main goal 
then they will be tempted to decide to ditch or &#39;duplicate and change&#39; a 
legacy code base rather than incrementally improve the legacy code base -
 and they would be among thousands of managers before them who have made 
that same decision. How many of those software teams are still around to tell the tale?&lt;br /&gt;
&lt;h2&gt;
Strategies for refactoring legacy code&lt;/h2&gt;
&lt;h4&gt;
Incremental&lt;/h4&gt;
The secret in refactoring existing code is that you want &#39;bang for buck&#39;. In other words you want a great return for the smallest investment. This is the key to the success of the refactor strategy - you only change &lt;i&gt;what you need to change now&lt;/i&gt;. The rest can wait till later. Over time the entire application may end up being refactored and enhanced. That&#39;s great but the secret is to make it awesome &lt;i&gt;over time&lt;/i&gt; with incremental changes - they are much more manageable and testing their effect and fixing side effects is much easier and indeed more viable than a &#39;big bang&#39; approach that touches lots of different parts of the code.&lt;br /&gt;
&lt;h4&gt;
Database Access - Persistence &lt;/h4&gt;
An immediate assumption by people who want to ditch the legacy code base and move onto a new or duplicated version of it is that it is impossible to refactor the old code base. It might use a database access layer that uses raw JDBC/ODBC or it may have no database layer at all: SQL statements appear littered throughout the code. One of the assumptions is that to move to an Object Relational Mapper (ORM) to gain the productivity benefits that they bring requires a new database schema - this is clearly not the case. Most ORM implementations in Java like Hibernate (JPA), DataNucleus (JPA/JDO) and others allow you to map to existing legacy database schemas. Each time you refactor a class to be persisted by an ORM instead of using JDBC/ODBC and SQL directly you actually decrease the size and maintenance cost of your code base. Most ORMs will play nicely within a system that accesses objects both through the ORM and through direct JDBC. This means that it&#39;s possible to migrate your code base to a new ORM oriented version in a piecemeal way over time - you don&#39;t have to convert the entire application over to ORM access before a new production release can be deployed.&lt;br /&gt;
&lt;h4&gt;
Multiple Products - single code base&lt;/h4&gt;
We touched on this earlier: it is desirable to have a single code base for the generation of product variants that are essentially very similar in function but might differ in titles, terminology, internationalization, color schemes etc.,&lt;br /&gt;
Clever use of patterns like the &lt;i&gt;factory&lt;/i&gt; and &lt;i&gt;template&lt;/i&gt; patterns allows code that used to include hard coded screen display or functional behavior can be refactored to deal instead with an abstracted interface. A product specific implementation of the interface is &#39;plugged in&#39; to the application during configuration. New variants of the product can be created very easily by simply providing an new, product specific implementation of the required interfaces and setting up appropriate configuration files to &#39;plug them in&#39;.&lt;br /&gt;
&lt;h4&gt;
Changing the User Interface framework&lt;/h4&gt;
A driving force for many a greenfield project is that the old user interface framework is technically &#39;extinct&#39; like the dinosaurs or no longer &#39;cool&#39; and much newer &#39;funkier&#39; ones are calling. Ideally, regardless of the legacy UI framework there would have been a well disciplined MVC strategy applied or at least a good separation between the underlying model and the user interface code. I say ideally but I do understand that the architecture of some UI frameworks (eg., JSP) often encouraged the inclusion of model and business code right into the UI elements. If you fell for this trap then you need to slap yourself in the face repeatedly with a wet fish!&lt;br /&gt;
&lt;br /&gt;
The evils of mixing UI and model or business code have been dealt with many times before and I know you have learned your lesson the hard way so I don&#39;t need to mention it again ... OK! What I will say, is that if you are starting with legacy code that suffers from this a good first step is to incrementally start a process of UI/Model separation. Create some separate model classes if you haven&#39;t already and start shifting model code, any JDBC/ODBC code and any SQL &#39;glue code&#39; from your UI elements into those model classes. After some pure model classes have been established you can attempt to incorporate a more modern UI framework by targeting the UI elements that only require model objects that have already been &#39;purified&#39;.&lt;br /&gt;
&lt;br /&gt;
If on the other hand your legacy code already has Model/UI separation then your task is much easier. If not already the case, then move the model into it&#39;s own package and move the UI code into a separate package.&lt;br /&gt;
&lt;br /&gt;
eg.,&lt;br /&gt;
&lt;br /&gt;
Package:&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;com.acme.myapp.onebigblob&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
becomes:&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;com.acme.myapp.ui.jsp&amp;nbsp; &amp;lt;- Any supporing JSP classes&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;com.acme.myapp.model&amp;nbsp;&amp;nbsp; &amp;lt;- Pure POJO model classes&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Once that is complete you can start creating UI elements using your more modern UI framework (Wicket, Tapestry, JSF etc.,). In most cases these should be able to coexist in the one code base (we&#39;ve done this with Echo and Wicket). The important thing is that while in transition both the legacy UI code and the new UI code both use exactly the same model classes - this is why we need complete separation of UI and model code. The model code must have no references whatsoever to any classes from either the legacy or the new UI frameworks. Create a separate package for the new UI framework (eg., Wicket). All classes related to that UI framework go into that package.&lt;br /&gt;
&lt;br /&gt;
eg.,&lt;br /&gt;
&lt;br /&gt;
Package:&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;com.acme.myapp.ui.wicket&amp;nbsp; &amp;lt;- All Wicket page/panel classes&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
If your app is non trivial then you should create subpackages under wicket to organize individual logically separate areas of functionality into their own package.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eg.,&lt;br /&gt;
&lt;br /&gt;
Package:&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;com.acme.myapp.ui.wicket&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;com.acme.myapp.ui.wicket.user&amp;nbsp; &amp;lt;- Login form, account preferences&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;com.acme.myapp.ui.wicket&lt;/span&gt;.borrow &amp;lt;- Book borrowing and related forms&amp;nbsp;&lt;/span&gt; &lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Together we stand, divided we fall&lt;/h2&gt;
&lt;h4&gt;
Divided we greenfield &lt;/h4&gt;
Choosing to perform a new greenfield implementation of the legacy code divides by creating two separate teams. One consequence of creating a new greenfield project with a new software team is that it can create an &#39;us and them&#39; scenario where the developers left on the legacy code team can feel like second rate citizens as they have been relegated to the relatively unexciting role of maintaining old code that uses a bunch of old technologies that no longer have the developer &#39;street cred&#39; that they used to.&lt;br /&gt;
&lt;br /&gt;
The developers on the greenfield project get to experience all the &#39;thrill and excitement&#39; of skilling up and using all the latest and greatest technologies and the benefits don&#39;t stop there - their resumes undergo a potentially very useful enrichment.&lt;br /&gt;
&lt;br /&gt;
Splitting the team into legacy and greenfield can cause divisions that can affect morale of developers &#39;stuck&#39; on the legacy team - especially the aspirational developers who want to improve their skill set and who want to help the company improve and enhance its software assets.&lt;br /&gt;
&lt;br /&gt;
In some teams there are developers who are happy to merely clock in each day and aren&#39;t at all interested in the challenges of learning to use new, exciting and usually, much more productive, technologies. The consequence of having these types of developers on any team won&#39;t be covered here: it would require a completely separate article to discuss issues related to that.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Together we refactor&lt;/h4&gt;
&lt;br /&gt;
A decision to perform a separate greenfield development will definitely create two distinct teams and can expose the company to the issues discussed above. On the other hand, a decision to refactor and enhance the existing code base as a unified team can create positive outcomes for everyone involved. The &#39;buzz&#39; created by the challenge of adding new features and incorporating newer tools and technologies affects everyone on the team. Eventually all developers will gradually be exposed to the new technologies so no one is being left at the station as the technology train departs - everyone goes along for the ride. When the &#39;together we refactor&#39; strategy is taken we remove the problem of developers on the legacy software feeling rejected or &#39;left behind&#39;.&lt;br /&gt;
&lt;h2&gt;
Tough call&lt;/h2&gt;
In this article I have related my two decade old, real world experience of the consequences of choosing &#39;code base duplication&#39; and &#39;greenfield development&#39; over refactoring and improvement of an existing code base. Hopefully this has been informative enough for 
software managers to help them understand that making a decision that is popular with their software 
engineers is not the same as making decisions, however unpopular,  that 
can drastically affect the financial viability of the software team in which those same engineers work.&lt;br /&gt;
&lt;br /&gt;
Unless you&#39;re a Microsoft or Google with unlimited financial resources to spend on a strategy that costs a lot of money and does not give any return in the short or medium term then you really need to make decisions that avoid major increases in the size and/or number of your software code bases because creating and maintaining software is not at all cheap. Choosing the correct strategy for managing the production of your company&#39;s 1&#39;s and 0&#39;s is a tough call.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Share your experiences...&lt;/h2&gt;
Please let us know of your experiences with software teams that had to make this crucial decision. You don&#39;t have to mention the company name but please let us know whether it was a small, medium or large enterprise and the decision they made and whether it was as success or not - i.e. did it make it before the greenfield window closed?</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/5628786233945316598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/09/greenfield-or-refactor-legacy-code-base.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/5628786233945316598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/5628786233945316598'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/09/greenfield-or-refactor-legacy-code-base.html' title='Classic software engineering mistakes: To Greenfield or Refactor Legacy code?'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-509976624617682276</id><published>2012-06-26T06:04:00.005-07:00</published><updated>2012-06-26T06:07:23.733-07:00</updated><title type='text'>As a Java UI Framework let me say Wicket Rocks!</title><content type='html'>Everybody knows that Wicket rocks as the best Java UI framework available. Sure there&#39;s people out there still using JSP (with/without struts), Spring MVC (Huh?), JSF, even raw servlets and a host of many other Java frameworks but let me repeat; &quot;Everybody knows that Wicket rocks as the best Java UI framework available&quot;.&lt;br /&gt;
&lt;br /&gt;
I suppose I should clarify that a bit.... everyone who has ever used Wicket knows that it rocks....&lt;br /&gt;
&lt;br /&gt;
You see, if you&#39;re a Java programmer who &quot;gets&quot; the whole Object Oriented paradigm and loves the reusability that comes from plug and play component architectures and from clever use of inheritance then you &lt;i&gt;simply won&#39;t find ANY other framework&lt;/i&gt; that satisfies your need for such architectural candy at the UI level but that still allows the visual candy that expert web designers can create when they have full control over HTML and CSS.&lt;br /&gt;
&lt;br /&gt;
All I can say is &quot;Get into it already&quot;. Get started. Read up on it. We&#39;ve been using Wicket since 2006 and have never looked back. The community is alive and vibrant and any issues are usually fixed within a day.&lt;br /&gt;
&lt;br /&gt;
The current release is version 1.5 although version 1.6 is under development. If you&#39;re starting out I&#39;d stick to version 1.5 as that and version 1.4 are what most developers are using at the moment.&lt;br /&gt;
&lt;br /&gt;
Here&#39;s the &lt;a href=&quot;http://wicket.apache.org/&quot; target=&quot;_blank&quot;&gt;Wicket website&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Here&#39;s an excellent piece on &lt;a href=&quot;http://www.devproof.org/wicket_best_practice&quot; target=&quot;_blank&quot;&gt;Wicket best practices&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/509976624617682276/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/06/everybody-knows-that-wicket-rocks-as.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/509976624617682276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/509976624617682276'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/06/everybody-knows-that-wicket-rocks-as.html' title='As a Java UI Framework let me say Wicket Rocks!'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-368391367943949169</id><published>2012-06-07T15:30:00.000-07:00</published><updated>2012-06-07T17:31:55.194-07:00</updated><title type='text'>Java package name structure and organization - best practice and conventions</title><content type='html'>When it comes to conventions regarding how to structure your packages in Java (or any other language in fact) there is a wide variety of opinion. Making the right decision can promote productivity, understandability, quality, flexibility and the construction of reusable frameworks.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve seen some people promote &#39;package by feature&#39; over &#39;package by layer&#39; but I&#39;ve used quite a few approaches and found &#39;package by layer&#39; much better than &#39;package by feature&#39; but further from that I have found that a hybrid: &#39;package by layer then feature&#39; strategy works extremely well in practice and here&#39;s why I think that&#39;s so.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;




Package by layer then feature&lt;/h3&gt;
The &#39;package by feature&#39; people tend to want to put all user interface, model classes, persistence classes (eg., DAO - if you&#39;re into self torture ;]) for a particular feature into the same package and according to many examples, the same directory. i.e. they don&#39;t use a different directory with the same package as is excellent practice for test source code because the test code can then access package level items to test them.&lt;br /&gt;
They then claim that their &#39;separation of concerns&#39; comes from having separate classes in the same package. Huh? That makes no sense whatsoever - &#39;separate&#39; in my book means completely separate - like get them the hell away from each other already!&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;




&#39;Plug and Play&#39; Layer implementations&lt;/h3&gt;
Let&#39;s say I design and implement a really sophisticated object model and I use a particular Java Web UI framework (say some popular but old fashioned one) to build a web app around this model. Consequences are:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&#39;Package by feature&#39; places my UI code in the same package as my model code.&lt;/li&gt;
&lt;li&gt;&#39;Package by layer&#39; places my UI code in a separate package to my model code.&lt;/li&gt;
&lt;/ul&gt;
Now let&#39;s say I want to change to a more modern component based OO web UI framework like Wicket. Consequences are:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&#39;Package by layer&#39; lets you start to build up a separate package for the Wicket UI classes. When it&#39;s working you can simply delete the old UI package and use the Wicket package instead - all the time nothing in the model package ever needs to get touched. This actually happened to one of our projects and it worked like a treat. We even had both frameworks in use in production at the same time as we gradually replaced screens using the old UI framework with Wicket screens.&lt;/li&gt;
&lt;li&gt;With &#39;package by feature&#39; this would have been an unholy mess - both JSP and Wicket classes in the same package/directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;h3&gt;




&#39;package by layer&#39; makes it easy to build reusable frameworks/libraries&lt;/h3&gt;
Another advantage of &#39;package by layer&#39; is that we can build shared libraries/frameworks that require both model and UI and we can distribute different versions of the framework with the same model code but with different UI libary implementations. You can&#39;t do this easily if you&#39;ve followed &#39;package by feature&#39; because all of your UI code for the different UI frameworks ends up in the same package/directory - ugly!&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;




Ugly breakdown of encapsulation and decoupling with &#39;package by feature&#39;&lt;/h3&gt;
There&#39;s one thing about &#39;package by feature&#39; that is promoted as a feature but is IMHO a complete breakdown of everything you&#39;ve ever learned. Using the same package for say model and test code is great for test code because it gives that code privileged access to test items with package level access in your model code which would be impossible otherwise. The very reason that those items were made package level was to enforce separation of concerns and reduce coupling. The developer of those package level items in the model wanted to allow access by other model objects in the same package but DENY access to other classes outside the package - like UI classes!&lt;br /&gt;
&#39;Package by feature&#39; places UI items directly in the same package as model objects meaning you&#39;ll be throwing away any language supported enforcement of decoupling that was placed in the model by the designer. You just can&#39;t promote a packaging scheme that breaks down language enforced decoupling and be taken seriously.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;




Naive assumptions about features and layers&lt;/h3&gt;
&#39;Package by feature&#39; assumes that the UI code for a feature has a nice one-to-one relationship with the model code for that feature. This goes against all of my experiences. In most apps a particular UI element pulls in various features of the model. Often there are &#39;core&#39; model objects that are used by just about every UI element. Which &#39;feature&#39; package do you place those UI elements in?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;




Summary of suggested approach&lt;/h3&gt;
So IMHO the best approach is a hybrid &#39;package by layer&#39; and then, after you&#39;ve split things up by layer, then the next level in your package splits them up by feature. I also use a slightly different approach for reusable &#39;frameworks&#39; or &#39;libraries&#39; than the one I use for apps.&lt;br /&gt;
&lt;br /&gt;
The format looks something like this:&lt;br /&gt;
&lt;br /&gt;
revdomain.moduleType.moduleName.layer.[layerImpl].feature.subfeatureN.subfeatureN+1...&lt;br /&gt;
&lt;br /&gt;
Where: &lt;br /&gt;
&lt;i&gt;revdomain&lt;/i&gt; Reverse domain e.g. com.mycompany&lt;br /&gt;
&lt;i&gt;moduleType&lt;/i&gt; [app*|framework|util] &lt;br /&gt;
&lt;i&gt;moduleName&lt;/i&gt; e.g. myAppName if module type is an app or &#39;finance&#39; if its an accounting framework &lt;br /&gt;
&lt;i&gt;layer&lt;/i&gt; [model|ui|persistence|security etc.,]&lt;br /&gt;
&lt;i&gt;layerImpl&lt;/i&gt; eg., wicket, jsp, jpa, jdo, hibernate (Note: model layer has only single implementation so leave out for model layer)&lt;br /&gt;
&lt;i&gt;feature&lt;/i&gt; eg., finance&lt;br /&gt;
&lt;i&gt;subfeatureN&lt;/i&gt; eg., accounting&lt;br /&gt;
&lt;i&gt;subfeatureN+1&lt;/i&gt; eg., depreciation&lt;br /&gt;
&lt;br /&gt;
*Sometimes &#39;app&#39; left out if moduleType is an application but putting it in there makes the package structure consistent across all module types.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eg., framework where some UI elements require access to common model objects in feature 1 but UI elements for feature2 model objects have very specific feature 2 references&lt;br /&gt;
&lt;pre&gt; com.mycompany.framework.myFrameworkA.model.feature1
 com.mycompany.framework.myFrameworkA.model.feature2
 com.mycompany.framework.myFrameworkA.ui.wicket
 com.mycompany.framework.myFrameworkA.ui.wicket.feature2&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
If you decide to make your framework available for multiple UI libaries you would add separate packages for each of these and ship each with the shared model code.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;com.mycompany.framework.myFrameworkA.ui.jsp
com.mycompany.framework.myFrameworkA.ui.jsp.feature2

com.mycompany.framework.myFrameworkA.ui.jsf
com.mycompany.framework.myFrameworkA.ui.jsf.feature2&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
I usually rename &#39;framework&#39; with &#39;util&#39; if it&#39;s more of a basic utility more than a framework with its own model and UI.&lt;br /&gt;
If you write code using a persistence technology that supports transparent persistence (e.g. an ORM) then your model code can be written in a fairly ORM agnostic way but you&#39;ll put supporting, ORM specific repository/service classes (or DAO classes if your into that) in a separate package for the persistence layer&lt;br /&gt;
e.g.&lt;br /&gt;
&lt;pre&gt;com.mycompany.framework.myFrameworkA.persistence.jdo.feature1
com.mycompany.framework.myFrameworkA.persistence.jdo.feature2

com.mycompany.framework.myFrameworkA.persistence.jpa.feature1
com.mycompany.framework.myFrameworkA.persistence.jpa.feature2&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
eg., for apps I drop the &#39;framework&#39; and follow mycompany with the app name:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;com.mycompany.app.myApp.model.feature1
com.mycompany.app.myApp.model.feature2
com.mycompany.app.myApp.persistence.jpa.feature2
com.mycompany.app.myApp.ui.wicket
com.mycompany.app.myApp.ui.wicket.feature2
&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/368391367943949169/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/06/java-package-name-structure-and.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/368391367943949169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/368391367943949169'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2012/06/java-package-name-structure-and.html' title='Java package name structure and organization - best practice and conventions'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-5143769018683845849</id><published>2011-08-04T01:57:00.000-07:00</published><updated>2011-08-04T02:38:57.743-07:00</updated><title type='text'>Modal X 1.3 for Wicket released</title><content type='html'>We have recently released a new version of the open source Modal window extension framework for Wicket called ModalX. We have also added an getting started guide, a live demo and an example application that demonstrates its main features.&lt;br /&gt;&lt;br /&gt;Feel free to leave any comments about the library here.&lt;br /&gt;&lt;br /&gt;Take a look at the ModalX home page here:&lt;br /&gt;&lt;a href=&quot;http://www.visualclassworks.com/content/list/o/76914/type/modalx&quot;&gt;ModalX Home Page&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;See the live demo here:&lt;br /&gt;&lt;a href=&quot;http://demo.visualclassworks.com/modalx&quot;&gt;ModalX Live Demo&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/5143769018683845849/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2011/08/modal-x-13-for-wicket-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/5143769018683845849'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/5143769018683845849'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2011/08/modal-x-13-for-wicket-released.html' title='Modal X 1.3 for Wicket released'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-8078461568962212348</id><published>2010-11-24T19:59:00.000-08:00</published><updated>2010-11-24T20:10:28.412-08:00</updated><title type='text'>Wicket DateTimeField - trick for young players</title><content type='html'>If, like me, you tried to use the DateTimeField component in the Wicket extensions library and saw an extra spurious TextField appear in your form and you didn&#39;t know why... then read on:&lt;br /&gt;&lt;br /&gt;It turns out that the DateTimeField is actually rendered as a Panel by Wicket. Being a field in a Form I naturally (according to what I saw in some online example code) just entered markup within my form like this:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size:85%;&quot;&gt;&lt;span style=&quot;font-family: courier new;&quot;&gt;&lt;br /&gt;&amp;lt;input type=&quot;text&quot; id=&quot;startDate&quot;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;but of course that can&#39;t work. Wicket can&#39;t render a panel into an input element (well not with any success at least!).&lt;br /&gt;&lt;br /&gt;It turns out that as a Panel the DateTimeField is best entered as a &amp;lt;div&amp;gt; in your markup:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size:85%;&quot;&gt;&lt;br /&gt;  &lt;span style=&quot;font-family: courier new;&quot;&gt;&lt;br /&gt;    &amp;lt;div id=&quot;startDate&quot; /&amp;gt;&lt;br /&gt;  &lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That allows the DateTimeField to be rendered correctly. Yeehah!&lt;br /&gt;&lt;br /&gt;Once again I want to congratulate the Wicket team on the world&#39;s best object oriented, component based, Java User Interface framework.... a big claim I know, but try it and I think you&#39;ll agree!</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/8078461568962212348/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/11/wicket-datetimefield-trick-for-young.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/8078461568962212348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/8078461568962212348'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/11/wicket-datetimefield-trick-for-young.html' title='Wicket DateTimeField - trick for young players'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-4069111992578570859</id><published>2010-08-31T23:53:00.001-07:00</published><updated>2012-09-10T20:06:18.349-07:00</updated><title type='text'>Feel like a Pizza?</title><content type='html'>Try Gabriel&#39;s Pizza and Ribs, using the munchopia online ordering service:&lt;br /&gt;
&lt;br /&gt;
Click here to: &lt;a href=&quot;http://gabrielspizzaribs.munchopia.com.au/&quot;&gt;Take a look&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/4069111992578570859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/08/feel-like-pizza.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/4069111992578570859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/4069111992578570859'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/08/feel-like-pizza.html' title='Feel like a Pizza?'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-2075848268248621193</id><published>2010-08-26T19:00:00.001-07:00</published><updated>2010-08-26T19:01:10.625-07:00</updated><title type='text'></title><content type='html'>Want to save the planet by saving on your power consumption?&lt;br /&gt;&lt;br /&gt;Then you need to install LED lighting in your home or business:&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.eoled.com.au&quot;&gt;EO LED&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/2075848268248621193/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/08/want-to-save-planet-by-saving-on-your.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2075848268248621193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2075848268248621193'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/08/want-to-save-planet-by-saving-on-your.html' title=''/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-8328747129879125155</id><published>2010-06-09T16:37:00.000-07:00</published><updated>2010-06-09T16:46:38.087-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Java C++ Wicket"/><title type='text'>New website for Step Ahead Software Development</title><content type='html'>The software development section of Step Ahead Software now has a new website. It&#39;s at:&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://develop.stepaheadsoftware.com/&quot;&gt;http://develop.stepaheadsoftware.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you need any software or web development let us know. We have been providing outsourced software development since 1994 and have a large range of satisfied companes from small businesses to large corporations like Canon and Lucent.&lt;br /&gt;&lt;br /&gt;We specialize in development in the following fields:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Windows Desktop &lt;/li&gt;&lt;li&gt;Web application development&lt;/li&gt;&lt;li&gt;Enterprise website application development&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;We are experts in the following languages, frameworks and databases:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Java, JDO, JPA, Wicket, Echo, JSP, J2EE, DataNucleus, Hibernate&lt;/li&gt;&lt;li&gt;C++, MFC, OWL, .Net, GDI, GDI+&lt;/li&gt;&lt;li&gt;MySQL, Oracle, MS SQL Server&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;We also have &#39;off the shelf&#39; modules for fast &#39;time to market&#39; of a variety of projects:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Entity management framework: For extremely fast implementation of websites or desktop applications that require users, people, groups, organizations, authentication, products, etc.,&lt;/li&gt;&lt;li&gt;Visual editing framework for creating applications that allow users to manage a visually oriented view of the topology of a particular problem space.&lt;/li&gt;&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/8328747129879125155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/06/new-website-for-step-ahead-software.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/8328747129879125155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/8328747129879125155'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2010/06/new-website-for-step-ahead-software.html' title='New website for Step Ahead Software Development'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-2933326427959045198</id><published>2008-11-20T09:10:00.000-08:00</published><updated>2008-11-20T09:49:57.121-08:00</updated><title type='text'>Step Ahead: Authentication in the Matrix!</title><content type='html'>A little while back one of our customers, Matrix Education, expressed a desire to improve the security and protection of their rather sophisticated intranet based enterprise management software. To this aim we discussed integrating our entity management system into their web based intranet application.&lt;br /&gt;&lt;br /&gt;Our entity management system is a powerful role based system that allows the management and authentication of entities including users, actors, organizations etc., It comes with a powerful, intuitive web based user interface to allow easy creation and management of entities and such as users and their roles and &#39;access rights&#39;. Our separate workflow management system plugs right into the entity management system for customers who want to manage processes  within their organization efficiently and automatically.&lt;br /&gt;&lt;br /&gt;The customer understood that integrating our well established &#39;off the shelf&#39; user management and authentication framework would provide a more reliable, high quality and less expensive alternative than building such a system from scratch.&lt;br /&gt;&lt;br /&gt;Yesterday, after only a few days effort we completed the integration of our entity management framework into their system so now their web based enterprise system has modern, powerful user authentication built seemlessly into their existing JSP based application. The use of our access managing servlet filter allows us to hook into the &#39;smarts&#39; of our entity management framework from just about any Java based web application including those built with servlets, JSP, Tapestry, Wicket and even Echo.</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/2933326427959045198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2008/11/step-ahead-in-matrix.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2933326427959045198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/2933326427959045198'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2008/11/step-ahead-in-matrix.html' title='Step Ahead: Authentication in the Matrix!'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-453104002322117484</id><published>2008-10-29T05:58:00.000-07:00</published><updated>2008-10-29T14:48:33.792-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CMS"/><category scheme="http://www.blogger.com/atom/ns#" term="media host"/><category scheme="http://www.blogger.com/atom/ns#" term="pagebloom"/><title type='text'>pagebloom&#39;s Media Asset Manager shines!</title><content type='html'>&lt;span style=&quot;font-size:130%;color:#3333ff;&quot;&gt;&lt;/span&gt;&lt;div&gt;&lt;br /&gt;&lt;span style=&quot;font-size:130%;color:#3333ff;&quot;&gt;Motivation&lt;/span&gt;&lt;br /&gt;About this time last year ago we conceived an idea to enhance our &lt;a href=&quot;http://www.pagebloom.com/&quot;&gt;pagebloom&lt;/a&gt; enterprise content management system (ECMS) to help manage the media assets (images, Flash intros etc.,) for the websites that it hosts. The idea was to create an advanced and powerful auto replicating media asset management system that could serve images from the most appropriate media host chosen from a range of available media hosts located around the world. That system was implemented and integrated with the pagebloom hosting service earlier this year. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;img id=&quot;BLOGGER_PHOTO_ID_5262691653027622578&quot; style=&quot;DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 140px; TEXT-ALIGN: center&quot; alt=&quot;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz1G0u0NsWrYRJRHSdWiUt4jGbdhJ6TGyu5o0yhhhcew08P5LHoo1FlrdGfnDf9P4st7w3I2AtYtxKIqQP0fYmuOtABZO387WqhNJ8W9oo_RtAjI3W9AOGnBrWga7h8ag2hmQV9i6C_2k/s320/mediaMgrWorld.png&quot; border=&quot;0&quot; /&gt;&lt;br /&gt;&lt;span style=&quot;font-size:130%;&quot;&gt;&lt;span style=&quot;color:#3333ff;&quot;&gt;How it works&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;If a particular host goes down or is under high traffic then the media assets can be served from alternative media hosts - all without site visitors experiencing any negative affects from the loss of performance on the degraded or lost media server.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size:130%;color:#3333ff;&quot;&gt;Concept payback!&lt;/span&gt;&lt;br /&gt;Since the integration of the new media asset manager none of the media hosts have experienced any problems, luckily. Well that &lt;em&gt;was&lt;/em&gt; until today! And pagebloom carried forth without a problem. The US based media server that went down was removed from the list of available servers and media assets for pagebloom hosted sites were transparently retrieved from alternative media hosts - all without site visitors noticing any problems. From the perspective of a software engineer it truly was a thing of beauty! We&#39;re all very satisfied and proud at how well our design was able to keep the pagebloom ECMS working perfectly even though a key component of the system was not available for a period of time.</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/453104002322117484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2008/10/pageblooms-media-asset-manager-shines.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/453104002322117484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/453104002322117484'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2008/10/pageblooms-media-asset-manager-shines.html' title='pagebloom&#39;s Media Asset Manager shines!'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz1G0u0NsWrYRJRHSdWiUt4jGbdhJ6TGyu5o0yhhhcew08P5LHoo1FlrdGfnDf9P4st7w3I2AtYtxKIqQP0fYmuOtABZO387WqhNJ8W9oo_RtAjI3W9AOGnBrWga7h8ag2hmQV9i6C_2k/s72-c/mediaMgrWorld.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7362939829754027611.post-5955427987586169078</id><published>2008-10-29T05:53:00.001-07:00</published><updated>2008-10-29T05:54:59.366-07:00</updated><title type='text'>Welcome to the Step Ahead Software blog</title><content type='html'>This blog is where we&#39;ll keep you up to date with all the news and happenings, product releases and product updates of Step Ahead Software.&lt;br /&gt;&lt;br /&gt;I&#39;m your host, Chris, so sit back, relax an enjoy the journey!</content><link rel='replies' type='application/atom+xml' href='http://stepaheadsoftware.blogspot.com/feeds/5955427987586169078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://stepaheadsoftware.blogspot.com/2008/10/welcome-to-step-ahead-software-blog.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/5955427987586169078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7362939829754027611/posts/default/5955427987586169078'/><link rel='alternate' type='text/html' href='http://stepaheadsoftware.blogspot.com/2008/10/welcome-to-step-ahead-software-blog.html' title='Welcome to the Step Ahead Software blog'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>