1) My program does draw about 90% (to hazard a guess) of the blocks it needs from code, based on dimensional information and other various options either selected by the user, or calculated by the program. These are all 3D blocks, and there are so many options involved that a block library would be prohibitively large and difficult to maintain. (The original version of my code, was LSP, in 2002, and used a block library. The old library has about 5000 blocks in it, but all the possible variations were never created, and the options have expanded since then, so again, to hazard a guess, my program is capable of creating in the neighborhood of 100,000 distinct blocks, if all the options are used).
2) That said, I mentioned these are 3D blocks, and some of the pieces that make up a block are complicated shapes that would be extremely tedious to draw in code, so I create those pieces in what I call a "Seed" file (offline file, in your post), and my code WblockClones them into the current drawing when it needs to, and inserts them into the parent Block definition at the right location, as it creates the parent block. At this time, there are about 80 blocks in my seed file. There used to be more, but I found the need to make some of the assemblies dimensionally flexible, so I broke them down into smaller pieces and made the code to put the pieces together with the currently selected dimensional info.
3) Just to make things as robust as possible (and I do have some code that is not for my specialized design program, but rather just for basic cad tasks like inserting 2D symbol blocks, or annotations) I have one InsertBlock Routine in my .NET code, that will find and retrieve the block from: 1- Inside the Current Drawing, 2-Inside the Seed DWG, 3-In AutoCAD's Search Path, 4-In any searchable drive on the users computer, including mapped network drives.
4) Finally, I would summarize by saying, in my opinion, of the options listed in your question, the only one I would suggest avoiding is keeping them in the template file. This makes the template file large (larger than it needs to be), and then you have to worry about the user purging a drawing, then running your code which requires a block that has been purged.
PS. For some (2D only) blocks, I have written some code which is an expansion of this Kean Walmsley Post, His post only handles code for one type of entity selection, and one type of entity output, but it demonstrates a technique (not rocket science, at all) to allow you to select entities in a drawing window, and put C# (or in my case VB) code on your clipboard to re-create each of the entities selected. This makes the coding for simpler objects much easier, even if you need to modify the code afterwards to make it respond to a dimensional variable instead of being a fixed dimension, for instance.
That approach obviously won't work for 3D Solids, because you can't get enough information from AutoCAD about the existing solid to recreate it.
Edit: Just to be clear, I don't need to worry about "Customer's" wanting to change things, because all my work is currently for use by my company only, and not for sale.
Dave O.