When Flash CS3 with AS3 was released in 2007, I convinced the company it was in their best interest to rewrite the entire Viewer in AS3 and I became the one and only AS3 Flash developer in the company. The other members when back to their previous duties or left the company. The most fundamental change in the rewrite was to use a plug-in approach based on dependency-injection instead of the all-in-one approach of the previous Viewer. I designed and implemented the entire dependency-injection framework myself with a little bit inspiration from the Java PicoContainer project including circular dependency resolution. This was before open-source dependency-injection frameworks became available. I added multi-phase initialization and multiple application instances to this framework and it turned out to be a truly winning combination for the company. From there I developed a set of runtime plug-ins and had 80% of the old Viewer functionality working in a few months with fewer bugs. This approach yielded a number of important benefits:
- Startup appeared to be much faster because the plug-ins could start rendering as soon as each was loaded. This alone increased customer satisfaction to new levels. (The previously Viewer didn't produce anything until everything was loaded and initialized which could take minutes and customers hated this.) The new Viewer didn't actually load any faster but the fact that it started rendering right away made it seem more responsive to our now happy customers.
- Features could be added or removed dynamically by adding or removing the plug-ins that implemented those features. This made sales and marketing very happy as they could unbundle the features and create multiple price points based what customers were willing to pay. In addition, there was no overhead loading a feature that wasn't needed and no way for customer to hack the product to enable features that were never included in the first place.
- QA loved this approach because there was no need to retest the entire product. They only needed to test new or modified plug-ins. This made for faster time-to-market speeds as-well-as improved reliability. Development times were sometimes longer because there was a bit more effort maintaining tight cohesion with loose coupling through carefully designed plug-in interfaces but the end result was more flexible and maintainable in the long run. Bug reports on the product came way down. In the last product update, QA found no bugs at all.
- I, as a developer, love this approach as I can develop and debug new plug-ins directly. Because there is no centralized loader, any plug-in can be put under a debugger directly because dependency-injection recursively resolves any dependences and loads only the minimum set of other plug-ins as needed. This eliminates all the problems of trying to debug a plug-in that isn't directly under the control of the debugger as would be the case with a centralized plug-in loader (using the Service Locator Pattern) where you would have to put the plug-in loader under the debugger and then try to get to the debugger to resolve debug symbols for a plug-in loaded at a later time indirectly by the loader.
- Multiple versions of the same plug-in could be loaded based on customer requirements. By the end of 2011, we had no less than two map plug-ins with different behaviors, two web services plug-ins, two static-overlay plug-ins, and four plug-ins that presented custom UI skins (including a holiday steam-punk skin for tracking Santa). And, all of this could be customized at runtime for each customer by merely choosing which plug-ins to include in a load map file. This also aids development as mock plug-ins can be used in place of real plug-ins for unit testing, (such as simulating the effects of a web server malfunction on the Viewer that would be difficult to simulate otherwise). The Santa Tracker uses a mock web services plug-in to provide canned data in just this fashion.
- Multi-phase initialization decouples plug-in initialization from the plug-in load order and guarantees that all plug-in initializations are deterministic. The idea was inspired by the SysV/Linux
initdrun-levels for O/S startup but I use the Chain-of-Responsibility and Abstract Factory patterns in my implementation. Plug-in z-order in the Viewer UI is also decoupled from load order so the z-order is deterministic and rooted correctly. Some plug-in UIs are rooted to the map layer such as map overlays and pushpins while others are rooted to the Flash screen such as the skins, menus and dialogs.