SharePoint architects and developers often wonder how best to design solution packages for long-term ease of use, especially through upgrade cycles. In a survey of SharePoint developers I found a range of strategies from one monolithic WSP to hold everything, up to practically one per feature which resulted in as many as 50 WSPs for a project. The variance depended on the developer's goals for maintenance. Application vendors want to keep things simple for admins, so a single solution per product makes sense. When downtime needs to be absolutely minimized and you need absolute granular control over each and every feature, maybe you want to maintain dozens of packages so that upgrading one will minimally affect the others.
But what I wanted was a strategy that recognized the average case rather than the extremes. From there, as with any taxonomy, specific implementations can either generalize up to the monolithic, or specialize down to the highly granular. Reza's strategy used 5 packages (follow the link for details), so I started there and through several trials arrived at the recommendations found here. Along the way, principles emerged to guide a general strategy.
Principles of SharePoint Development Structures and Packaging
- You need naming standards. Plan your .NET namespaces, feature names, content types, list ID numbers, internal names for site columns, custom field types, and every other possible element so that related work sorts together in the \bin, the Global Assembly Cache (GAC), the \TEMPLATE\FEATURES subtree, and any other folder, and is easily differentiated from other work as you walk through elements in code. Across projects you need to maintain a central index of List Type IDs (starting from 10000) to avoid collisions.
- Think of your source control as a series of snapshots kept in step with your WSP packages. If you need to roll back to a previous version, that means going back to a previous set of WSPs, and that means being able to compare that complete working code set with the broken one. More often you'll see the benefit in code reviews, during which any decent differencing utility will help you review the sections that changed from the old to current version.
- It is preferable to have more (not fewer) Visual Studio projects and assemblies (DLLs) packaged as more (not fewer) Features. The benefit is that it is often (though not always) sufficient to only regression-test the assemblies that changed from version-to-version, rather than to build do-it-all assemblies [of course this depends on how specialized or loosely-coupled your assemblies are in the first place]. When the changes of many developers need to be tested in a release, it is harder to isolate the work of each in monolithic projects and assemblies. If your namespaces are consistent so your project assemblies are easily identifiable, then server administrators should prefer this selective strategy too; it's when dozens of assemblies with random names are scattered across the GAC that people justifiably complain.
- It is preferable to package your VS projects and features as fewer (not more) WSPs. The more WSP packages you have, the longer they take to install, and the more complicated your deployment scripts. That said, smaller teams will tend towards fewer WSPs, and larger teams will have more. But keep it within reason. Each developer will own different areas of functionality, and people "get in each other's way" less often when team roles map to distinct solutions.
- You do not need to create WSPs to develop, you do need WSPs to deploy. It takes a long time to retract, delete, add, and deploy a solution, and the cycle provides no benefits in a dev environment. Obviously, this applies until it's time to test packaging and deployment. If you only need to test a code change then copy the updated assembly to the GAC and recycle the Application Pool. If deploying to the \bin, you can even skip the recycle. You do not need to redeploy an entire solution to test discrete changes to code or ghosted aspx files.
- When deploying solutions with STSADM you have the option of going through the full retract, delete, add, and deploy cycle or to use the "-o upgradesolution" switch. While upgradesolution is faster than going through the complete cycle, it does only upgrade existing features. Any new features in the solution will need to then be installed individually to each Web Front End (WFE) in the farm. Only the complete cycle ensures that new features will be installed automatically to every WFE in the farm.
- It ain't easy to version Content Types. Your options are to a) once deployed, never upgrade a content type; b) only update content types through the UI; or c) update content types in code. I prefer to upgrade content types in code, albeit through a generic tool (based on code by Søren Nielsen wrapped in an STSADM extension by Gary Lapointe) that propagates the content type schema down ot existing instances. John Holliday also described an iterative approach to content type updates in chapter 11 of Professional SharePoint 2007 Development.
There are four installation and upgrade operations to plan for:
1. Installing and activating features (packaged as WSPs),
2. Creating content with features (e.g. site provisioning and list instances),
3. Deactivating and removing features, and
4. Removing the content created by features.
During an upgrade you normally don’t want to alter existing content, you want to remove current functionality and replace it with a newer version. If you keep these four operations in distinct packages, it’s easy. When you start mixing list definitions with list instances, or mixing deactivation with cleanup, you can no longer manage these as distinct operations. Therefore guidance to "always clean up after yourself in the feature deactivated receiver" is wrong if your goal is to update functionality without affecting existing content.
Recommended solution packages (WSPs)
1. Global (assemblies and server controls)
2. SP Administration features (e.g. Central Admin features, web services, and STSADM extensions)
3. SP Base Features (elements re-used among projects and site types)
4. SP Site Features (e.g. stapled features or site definitions)
5. Chrome and Branding (e.g. gallery content including master pages, style sheets, and layout pages)
6. Instances (e.g. site-provisioning feature receivers, features containing list instances, and cleanup receivers)
Notice the narrowing trend in this order from farm-wide down down to specific instances. Another way to see it is as a layering of features from a foundational base up through presentation. Any of these packages can be divided further. For example you may want to manage separate solutions for instance creation vs. instance cleanup. Or you may want to maintain different site definitions and their constituent features (or stapled feature sets) in separate packages. Also, any of the elements can move around where it makes sense. For example, if you prefer to put list instance features (usually in #6) in the same solution as their list definitions (in #3 or 4), do it. Just be sure you don't reactivate your instance features (and create new redundant lists) every time you upgrade your list definitions. The decision is whether to simplify the maintenance of code, or maintenance of your deployment scripts.
When creating the actual Visual Studio Solutions to support each of these packages, it helps to name the folders to sort in a natural order, for example:
I hope you find this to be a useful strategy for organizing your own solutions and packages. As always I'd love to hear feedback about other approaches, exceptions to the rules, and other thoughts on the topic. Another time we'll dig into sample solutions to further explore namespace structures and automated build strategies.
Thank-you to Reza Alirezaei, Jake Dan Attis, and Dan Larson who shared their strategies on solution design. Also thanks to Robert Bogue for reminding me of the weaknesses of upgradesolution and Brendon Schwartz for rekindling the whole topic, that was the kick I needed to finally commit these ideas to a post.