locked
Assebly versions, backward compatibility RRS feed

  • Question

  • My product ships an infrastructure assembly(let's name it as "api_common.dll"). This is a strongly-named assembly. It is used by the product runtime, as well as, it is used by the WCF services (end user applications) that my product can host (in CLR hosted inside JVM).

    I would like these end-user apps not to bundle "api_common.dll", rather use one the same version used by the product runtime. This will help keep end-user app size smaller (note that multiple user-apps can run on this product runtime and if each app needs to bundle api_common)

    With this background:

    1. What are the recommended ways/best practices around backward compatibility for infrastructure libraries?

    specifically -

    a. when one is expected to update assembly version?

    b. What is considered as backward compatibility breakage - any change to assembly Vs. changes to public interfaces?

    c. Is not using strongly-named assembly nature good idea in this case?

    2. Counter argument to the approach discussed above is - "end user app should bundle exact same assembly that it is compiled" and "any change (as small as correcting typo in error msg) to api_common.dll" warrants change to the version of the assembly". I would like to hear opinions from .Net experts on this.

    P.S. : I do NOT have an option of keeping this api_common.dll in GAC


    • Edited by sjtib Tuesday, July 17, 2012 6:06 PM
    Tuesday, July 17, 2012 5:54 PM

Answers

  • Just my opinion:

    1a - Depends on you. Safest: each build. Minimal: Public API update.

    1b - In extreme cases any innocent change to an assembly has potential to break some customers. It depends on you how you manage risk and what kind of changes you do/do not consider breaking. In general any observable change to public API behavior should be considered breaking. All others might be considered non-breaking.

    1c - Without strong-name you loose versioning.

    Some scary breaking changes examples I encountered:

    A. Public API implementation has a bug and instead of returning true/false, always returns true. When you fix it, you find out that some customer code misused the API, calls it with wrong args and depends on the fact it always returned true. After your fix the customer code is broken.

    B. You find out that your API throws certain cryptic exception on invalid input. You fix it to throw something reasonable (for easier debugging and troubleshooting) and find out that customer code did catch exactly the cryptic exception and by changing it you broke them.

    C. You find out that your deep internal function returns value 0 and success on invalid input (potentially security bug). You fix it to return proper error code and find out that one of the callers had another bug that passed wrong arguments (sometimes). The error code is unfortunately surfaced back up to the public API and there is customer code that is broken now - API that used to ignore some internal error and 'worked just fine' form customer's point of view now sometimes fails.

    D. You change shape of your private COM interface used for communication between 2 DLLs (1 DLL is private without any public API). You find out that some customer reverse engineer your code and hijacks your internal interface to provide some additional value to their customers. Their code is broken - different shape or behavior of the COM interface now causes crashes. You think it is ok - they should not reverse engineer your code (violation of licensing/EULA). But their customers don't know about it and blame you for breaking them. ... This one is really corner case and probably applicable only to few SW products (like CLR), but it is an example that anything has a chance to break someone. You just have to live with it and set the bar somewhere.

    -Karel

    • Proposed as answer by Mike Feng Thursday, July 19, 2012 8:41 AM
    • Marked as answer by Mike Feng Friday, July 27, 2012 9:46 AM
    Wednesday, July 18, 2012 4:19 AM

All replies

  • Just my opinion:

    1a - Depends on you. Safest: each build. Minimal: Public API update.

    1b - In extreme cases any innocent change to an assembly has potential to break some customers. It depends on you how you manage risk and what kind of changes you do/do not consider breaking. In general any observable change to public API behavior should be considered breaking. All others might be considered non-breaking.

    1c - Without strong-name you loose versioning.

    Some scary breaking changes examples I encountered:

    A. Public API implementation has a bug and instead of returning true/false, always returns true. When you fix it, you find out that some customer code misused the API, calls it with wrong args and depends on the fact it always returned true. After your fix the customer code is broken.

    B. You find out that your API throws certain cryptic exception on invalid input. You fix it to throw something reasonable (for easier debugging and troubleshooting) and find out that customer code did catch exactly the cryptic exception and by changing it you broke them.

    C. You find out that your deep internal function returns value 0 and success on invalid input (potentially security bug). You fix it to return proper error code and find out that one of the callers had another bug that passed wrong arguments (sometimes). The error code is unfortunately surfaced back up to the public API and there is customer code that is broken now - API that used to ignore some internal error and 'worked just fine' form customer's point of view now sometimes fails.

    D. You change shape of your private COM interface used for communication between 2 DLLs (1 DLL is private without any public API). You find out that some customer reverse engineer your code and hijacks your internal interface to provide some additional value to their customers. Their code is broken - different shape or behavior of the COM interface now causes crashes. You think it is ok - they should not reverse engineer your code (violation of licensing/EULA). But their customers don't know about it and blame you for breaking them. ... This one is really corner case and probably applicable only to few SW products (like CLR), but it is an example that anything has a chance to break someone. You just have to live with it and set the bar somewhere.

    -Karel

    • Proposed as answer by Mike Feng Thursday, July 19, 2012 8:41 AM
    • Marked as answer by Mike Feng Friday, July 27, 2012 9:46 AM
    Wednesday, July 18, 2012 4:19 AM
  • Karel,

    All of your points and examples are very valid. The situation I'm dealing with can be better explained with following examples:

    "Let's say IIS ships "api_common.dll" for certain API for end user applications. Also IIS runtime makes use of API from "api_common.dll". How does one balances between

    a. expecting every user app to package "api_common.dll"

    b. Using same instance of api_common.dll that is used by IIS runtime

    thanks,

    sj

    Wednesday, July 18, 2012 5:59 AM
  • Hi Sj,

    My personal opinion is b: Using same instance of api_common.dll that is used by IIS runtime. It will be easy to maintain.

    Have a nice day.


    Ghost,
    Call me ghost for short, Thanks
    To get the better answer, it should be a better question.

    Thursday, July 19, 2012 8:48 AM
  • Hi Sjtib,

    Do you have any update?

    Do you agree with Crazyghost_von?

    I have marked Karel's suggest as answer, if you don't agree with it, please feel free to unmark and follow up.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, July 27, 2012 9:46 AM