On Fri, Nov 5, 2010 at 7:27 AM, Phillip Hellewell <[email protected]> wrote:
> Hi, > > I need some advice on how to handle dependency compatibility issues. > E.g., if A => B => C (and A => C directly too), my understanding is > that Maven will allow me to update A to depend on a new version of C, > even though B still depends on the old version. > > But what if B is incompatible with the new version of C? > > Here are some ways C might be changed: > 1. An implementation change (e.g., a function in a .cpp file). > 2. An interface change (e.g., class definition in a .h file). > 3. A data structure change (e.g., a data member inside a class). > > #1 is the kind of change that usually doesn't cause any problems at > all. A can depend on a new version of C and that doesn't cause any > problems for B. > > #2 is the kind of change that (hopefully) will cause a compile or > linker error when building A. If so, that's fine. If not, this could > be a serious problem. > > #3 is the kind of change that unfortunately will likely not manifest > itself until the middle of runtime, probably with an unexpected > crash!! > > An example of #3 is a custom string class. Suppose I change the > internal structure of the string class to include other data members. > Suppose that the string class is used heavily as parameters and return > values in the interfaces of A and B. Obviously "very bad things" will > happen if A is using one version of the string class whereas B is > using another. > > One way to mitigate #3 in C/C++ projects is to stick with strictly > plain C types (int, char*, etc) in all interfaces. But suppose we > have way too much code to go back and change that now... > > Another example I didn't mention yet is what if C produces dlls and > headers, and some functions are inlined in the headers. Updating A to > a new version of C means that B would use a mixture of implementation > from C. It would use the new implementation in C's dlls, but it would > use the old implementation of C's inline functions that got inlined > into B's dlls. > > What is the best way to handle all this to avoid serious runtime > issues? One way I know of is to lock down versions with a range like > [1.0.0.1]. But if my dependency tree is very tall, that means I may > have to spend a lot of effort rebuilding many components when a > low-level component changes. Since most changes are > implementation-only changes (#1), it would be nice to avoid that most > of the time. > > Is it a common practice to solve this problem using version ranges > such as [1.0.0-1.0.1), i.e., 1.0.0.x, and bump the 4th number for > implementation-only changes, and bump the 3rd number for interface or > other breaking changes? But what if a programmer makes a breaking > change and forgets to update the 3rd number, or they don't even > realize that what they are changing constitutes as a breaking change? > There's nothing you can do to prevent this, right? > I would do 2 things: First - remove the direct dependancy from A => C. Let maven pick up 'C' via transitive dependencies. 2nd, update your parent pom with a dependency management section to secify exact version numbers of all your artifacts. Then update the POM in A, B and C to Remove their version numbers. This causes the version numbers listed in your dependency management section to be used. Then when its time to update things, just change the version # in your dependency management section. build and your done.
