Wednesday, July 2, 2008

Why Install Multiple MSIs

One of the most common questions I get from people in our organization is "Why do we install multiple MSIs?" These people want to understand why our lab builds many different MSIs instead of creating one MSI that installs all the software, or more accurately, one MSI for each product release.

There are many benefits to doing this and many downsides, all of which can be argued, but there is only one really improtant reason that made us change our install design to use multiple MSI files: compatibility between our releases.

Our company makes hardware products, each of which comes with a software CD that includes drivers and some applications that let the customer use the product. To give a better user experience and to simplify development and testing, our lab started sharing the applications between all of our product CDs. If a user purchases two of our products, they get one copy of the application that works with both products. This makes sense, right?

So, this means that we're going to be sharing binaries that get installed between product CDs. There were a couple options for doing this:
  1. Create a single install CD that supports all the products
  2. Have a single MSI for each install CD that shares components with MSIs on other CDs
  3. Create an MSI for each "application" that gets shared and an MSI that is specific to each CD to install CD specific pieces.

Option 1 doesn't work for our lab, because there are too many products on release schedules that are too different. Also, it wouldn't be a great user experience for any user who buys one of our products to have to install drivers and support for 100 other products at the same time.

The problem with option 2 is basically that Windows Installer does a really good job of reference counting files, but not reference counting "features" or "applications." Let me explain... Let's pretend one of the applications that we're installing is texteditor.exe. Let's say we have two products, product A and product B. Let's say we'll create one MSI to go on each of the products' CDs: A.msi and B.msi. A.msi and B.msi will then each have a component to install texteditor.exe, that will have the same component ID and will install the file to the same location. Most likely there would be a texteditor merge module that has that component. Installing and uninstalling the two MSIs on the same machine works fine, leaving working software, because the texteditor.exe component is reference counted by the two MSIs.

Well, everything is working in this example, so what's the problem? Let's pretend that version 2 of texteditor.exe includes more functionality and now depends on textformat.dll. The texteditor merge module adds a new component to install the new dll and product C's MSI gets built that uses the new version of texteditor. Now when you install C.msi and A.msi, texteditor.exe is version 2 and works fine, because textformat.dll is on the system. But that dll is reference counted only once, by C.msi. If the user chooses to uninstall C.msi (and leave A.msi), texteditor.exe stops working even though product A is still installed. There are a couple workarounds for this problem: (a) make texteditor.exe gracefully degrade to its previous functionality if the dll isn't present, (b) install texteditor.exe v2 side-by-side with v1, or (c) force the customer to uninstall and reinstall all products. (c) is a crappy user experience, and with the number of products we sell and incidence of overlap, it just doesn't make sense. (b) was not a good option, because we had the desire to give the user only 1 instance of each application, because it wouldn't be clear to the user which version of the app to use with each product. We wanted the user to have only one suite of applications that would function with all installed products. (a) seems like a reasonable idea, but the applications we were installing were much more complicated than a simple text editor, and we made a technology change where we went from C++ to .NET applications, which changed almost all of the files that got installed -- except for the name of the exe that had shortcuts in the start menu. So, those exes would need to contain a whole boatload of logic (and code) to determine what functionality it could make available to the user and it would have to carry around all the old code.

So, instead of dealing with all these problems with our option 2, it made more sense to go with option 3 -- make each application a standalone MSI that would get upgraded independently of the rest of the software solution. Then each product CD would include a ProductA.msi and a TextEditor.msi and an MSI for everything else that was shared.

One of the other big benefits of this design was that each "application" or "feature" could be developed and tested independently. Then each product CD could pick up a fully tested MSI. Also each team creating an application could build their MSI on a separate schedule and wouldn't have to depend on products A, B, C, etc. to build MSIs so they could test their applications.

So, that's the big reason why we chose to deploy our software in a set of several MSIs.

But, the big problem that this design created was how to get all the MSIs installed and uninstalled. Which is a much longer discussion...