Lessons Learned Migrating Zend Framework 1 to 2

At Soliant Consulting we recently completed a substantial web development project for a global retail site which entailed migrating Zend Framework from 1 to 2. We updated the application from its existing Zend Framework 1 (ZF1) implementation to a new Zend Framework 2 (ZF2) implementation. We provided contract support for a customer who nominally produces everything in-house, so I will not mention any specifics of the project, but there are quite a few interesting generalities I can share with regard to the experience of migrating ZF1 to ZF2.

Project Goals

The legacy application was originally built over six years ago, and was in active daily use, with active feature enhancements added during its entire life cycle. One of the driving requirements was an information security department mandate to stop using Zend Framework 1, as it is already past the official end-of-life. Our client wanted to stay with Zend Framework, as it has served them well. Another high priority was to add some new features to support changes in the business. Both of these considerations presented the opportunity to consider some additional non-functional requirements, having to do with upgrading the development process to improve maintainability, quality, and speed of delivery for new features.

Main Goals

  • InfoSec requirement to migrate the application from ZF1 (Zend Framework 1) to ZF2 (Zend Framework 2)
  • Add new features to support new business requirements
  • Port most of the existing business logic
  • Maintain external legacy system integrations (both inputs and outputs)

Additional Goals

  • Pay down technical debt
  • Convert the VCS from SVN to Git
  • Implement new development work flow incorporating Vagrant
  • Remove deprecated code

Migrating Zend Framework – Conversion Vs. Rewrite

As part of the initial scoping discussions, our team considered a few different technical approaches. In very general terms, they boiled down to an A/B choice of either porting the ZF1 code into a new ZF2 app, or doing a full re-write. Since there is no direct migration path from ZF1 to ZF2, and the application is quite complex, there were two strikes against porting from the outset. In this case, the final consideration was the coding style of the original application.

There were two things that made it a less than ideal candidate for porting with shims or wrappers. First, the templating library was unusual and quite custom. Second, there was a great deal of procedural business logic built into the controllers. These facts made it a fairly clear that the long term cost would be lower if we invested in a full rewrite. Having launched the new version several weeks ago, and already begun the process of incremental enhancement, there is broad consensus that we made the right choice from a business value perspective.

Reflecting back on the old system compared to the new system, there are a few interesting metrics worth looking at.

Application Metrics: Before and After

Lines of Code

If you search the web for opinions about the value of measuring lines of code (aka LOC), you’ll find plenty that consider it a bit of a meaningless metric with regard to things like quantifying productivity, but it’s useful for the purpose of comparing the two code bases. We consider it good to see this metric drop, as that generally indicates removal of redundancy and/or lowering of complexity. We don’t actively track this metric during development, but now that the new version has been released, it is interesting to reflect on it a little.

As you can see in the `cloc` summaries of the before and after here, the total number of lines went down about 15%, while the number of files went up by almost 60%. We consider both of these changes to be good things, because it suggests that the code is less complex overall (fewer lines of code), with a better separation of concerns (split into more focused classes).

Before

$ cloc application/
     625 text files.
 
https://github.com/AlDanial/cloc v 1.66  T=2.89 s (206.8 files/s, 21648.7 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
PHP                            308           5087          12442          34078
Smarty                         289           1218            140           9525
-------------------------------------------------------------------------------
SUM:                           597           6305          12582          43603
-------------------------------------------------------------------------------

After

$ cloc module/
    1017 text files.
 
https://github.com/AlDanial/cloc v 1.66  T=4.82 s (196.7 files/s, 11471.0 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
PHP                            624           5847          10817          31151
Twig                           324            727            387           6356
-------------------------------------------------------------------------------
SUM:                           948           6574          11204          37507

Cyclomatic Complexity

Cyclomatic complexity is a software metric, used to indicate the complexity of a program. It is a quantitative measure of the number of linearly independent paths through a program’s source code. Cyclomatic complexity may also be applied to individual functions, modules, methods or classes within a program. Lower complexity is considered better than higher complexity. See the Wikipedia article for more details.

We plotted the before and after cyclomatic complexity of the classes and the individual methods. As you can see, we reduced the maximum class and method complexity by more than 75% with the most complex class falling from 647 to 192 and the most complex method falling from 146 to 42.

Before

FMZS-blog-complexity-before
(click image to enlarge)

After

FMZS-blog-complexity-after
(click image to enlarge)

Change Risk Anti-Patterns

The Change Risk Anti-Patterns (aka CRAP) Index can be calculated using PhpUnit, and is based on the cyclomatic complexity and code coverage of a unit of code. Code that is not too complex and has an adequate test coverage will have a low CRAP index. The CRAP index can be lowered by writing tests and by refactoring the code to lower its complexity. See the PhpUnit manual for more details.

Looking at the top 8 class and method CRAP scores before and after, we can see that the scores improved over 90%. The worst class improved from 419k to 37k, and the worst method improved from 21k to 2k.

Before

FMZS-blog-risk-before
(click to image to enlarge)

After

FMZS-blog-risk-before
(click image to enlarge)

Unit Tests

The legacy system had zero test unit test coverage. Given it’s implementation style, it would have been very challenging to cover. In the refactor, we didn’t increase test coverage significantly, but we did increase testability quite significantly by reducing class and method complexity, and by consistently implementing inversion of control (IOC) via the ZF2 service manager. The already significant improvement in CRAP score will improve even more as test coverage increases, since the CRAP score factors in test coverage.

Lessons Learned

We went into this project with an open mind about which technical approach to adopt: either porting the legacy code with shims, or starting with a clean slate. The potential benefits of the former approach manifest mainly as lower short term cost and lower regression risks, since the original business logic does not have to be disturbed (when applied appropriately). The primary risks with this approach are two-fold. First, it means front-loading a great deal of technical debt (since the project will have to be maintained in both coding styles indefinitely), which will mean that enhancements and bug fixes in the future are likely to be more costly and error prone. Second, even in a well encapsulated architecture, it is likely that there is some business logic that exists outside of the service classes, which means that the team may end up being forced to partially or substantially rewrite legacy code anyway. This results in a straddling of the old architecture and new architecture, which defeats the original premise, and undermines the lower regression risk advantage.

In the case of this project, we recognized early in the process that directly porting the ZF1 code was going to be more costly and harder to extend and maintain. While in some cases, porting ZF1 code into a ZF2 code base with shims could make sense, due to the specific implementation choices in the legacy application, we planned for a total rewrite up front. In the end, the project launched on schedule with all of the primary short term goals met. But more importantly, we met the secondary goals of reforming the development work flow, and dramatically improving things for the hands-on maintainers of the system.

We reduced deployment risks by introducing Git workflow, Vagrant, modular architecture, and deployment automation scripts. We improved the maintainability by fully embracing inversion of control with factories and the ZF2 service manager. We simplified continuous delivery of new features by lowering the complexity of the code base, as indicated by static analysis metrics. Of course we are happy that the primary business objectives were met, but as practitioners, we are particularly gratified that we were able to position the project on a firm development-process footing that will pay dividends for years to come.

3 thoughts on “Lessons Learned Migrating Zend Framework 1 to 2”

  1. Jeremiah,

    Good article and it verified that the work I just did to move one of our artist web sites from ZF1 to ZF2 as a rewrite, was the right thing to do.
    In addition we gained some advantages, like using zfcuser for registration and authentication and using Socialnick to allow registration and authentication using Facebook (and many other social apps as well).
    Hope you and your family are well.

    Cheers, Chuck

  2. Pingback: ZF3 ΓÇô Zend Framework 3.0 Released – FMT Network

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top