tags:

views:

461

answers:

11

Assume you have five products, and all of them use one or more of the company's internal libraries, written by individual developers.

It sounds simple but in practice, I found it to be very difficult to maintain.

How do you deal with the following scenarios:

  1. A developer unintentionally introduces a bug and breaks everything in production.

  2. Every library has to mature, That means the API needs to evolve, so how do you deploy the updated version to production if every developer needs to update/test their code while they are extremely busy on other projects? Is this a resource and time issue?

  3. Version control, deployment,and usage. Would you store this in one global location or force each project to use, say, svn:externals to "tie" a library?

I've found that it is extremely hard to come up with a good strategy. My own pet theory is this:

  1. Each common library has to have a super-thorough set of tests or else it should never be common, even if it means someone else duplicates the effort. Duplicate untested code is better than common untested code (you break only one project).

  2. Each common library has to have a dedicated maintainer (can be offset by a really good test suite in a smaller team).

  3. Each project should check out the version of the library that is known to work with it. This means a developer does not have to get pulled away to update API usage, as the common code gets updated. Which it will be. Every non-trivial piece of code evolves over months and years.

Thank you for your thoughts on this!

+5  A: 

I don't agree with this:

Duplicate untested code is better than common untested code (you break only one project).

If you are all equally likely to create bugs by implementing the same thing, then you'll all have to fix potentially different bugs in each instance of the "duplicate" library.

It also seems that it'd be much faster/cheaper to write the library once and, instead of having multiple other teams write the same thing, have some resources allocated to testing.


Now to solve your actual problem: I'd mimic what we do with real third-party libraries. We use a particular version until we're ready, or compelled to upgrade. I don't upgrade everything just because I can--there has to be a reason.

Once I see that reason (bug fix, new feature, etc.), then I upgrade with the risk that the new library may have new bugs or breaking changes.

So, you're library project would continue development as necessary, without impacting individual teams until they were ready to "upgrade".

You could publish releases or peg/branches/tag svn to help with all this.

If all teams have access to the bug tracker, they could easily see what known issues exist in the upgrade-candidate before they upgrade, too. Or, you could maintain that list yourself.

@Brian Clapper provides some excellent guidelines for how to run your library as a project in his answer.

Michael Haren
A: 

I think that one shared library is better than 3 duplicate ones (and 1 tested is definitely better than 3 untested). That's because when you find and fix problem, this makes the whole application area more solid (and development and maintenance are more efficient).

BTW, that't one of the reasons (apart from contributing back to the community) why our company exposes our .NET shared libraries to the public as open-source.

Plus, there's less code to write. And you can designate one dev to enforce good development practices on the library and its usages (i.e. through code contracts enforced on the unit tests within library consumers). This improves quality and and reduces maintenance costs.

We store shared libraries as binaries in the solution. That comes from the logical requirement that any solution has to be atomic and independent (this rules out svn:externals links).

API compatibility is not an issue at all. Just let your integration server rebuild and retest the whole product stack (while updating all the inner references and propagating changes) and you'll always be sure that all internal API's are solid. And whomever breaks the API has to either fix it or update the usages.

Rinat Abdullin
For a library of more than a certain size: one shared library is only better than 3 duplicate ones if it has been tested. If there is no test suite for the shared library, then it is probably more trouble than it's worth, because changes can break other dependent projects in unexpected ways, and because bugs won't be caught until late in the development cycle.
Craig McQueen
+13  A: 

You have a competing set of goals here. First, a library of reusable components must be open enough that people from the other projects can easily add to it (or submit components to it). If it's too difficult for them to do that, they'll build their own libraries, and ignore the common one, leading to a lot of duplicate code and wasted effort. On the other hand, you want to control the development of the library enough that you can ensure its quality.

I've been in this position. There's no easy answer. However, there are some heuristics that can help.

  • Treat the library as an internal project. Release it on regular intervals. Ensure that it has a well-defined release procedure, complete with unit tests and quality assurance. And, most important, release often, so that new submissions to the library show up in the product frequently.
  • Provide incentives for people to contribute to the library, rather than just making their own internal libraries.
  • Make it easy for people to contribute to the library, and make the criteria clear-cut and well-defined (e.g., new classes must come with unit tests and documentation).
  • Put one or two developers in charge of the library, and (IMPORTANT!) allocate time for them to work on it. A library that is treated as an afterthought will quickly become an afterthought.

In short, model the development and maintenance of your internal library after a successful open source library project.

Brian Clapper
+1  A: 

Treat the development of the libraries like any other product. Each library has its own repository, its own releases and version numbers. The compiled and officially tested versions of the library are also kept in the repository. Document features and changes from version to version.

Then use the libraries like you would using third party libraries. Your product uses only fixed versions of the compiled libraries. Switch to a new version when you really need to and be aware that this involves more testing. Add the versions you use to your version control.

When you find a bug or require a new feature in a library, a new version or sub-version is created. Using a version control system like svn makes this easy. When you need the source code for debugging purposes, export it and include it in your projects, but do not change it there, but fix problems in the libraries' repositories.

This way, every team can contribute to the libraries without endangering the work of the other teams. Switching versions is done deliberately and not by accident.

Sebastian Dietz
+2  A: 

I used to work in a similar situation to what you're describing, only my company had dozens of software products. I worked on the team that was responsible for maintaining and upgrading the core set of libraries that everyone else used.

We dealt with those scenarios as follows:

  1. Test the heck out of the core libraries. Maintaining duplicate code is a nightmare. You're not just maintaining the core and one copy. Somewhere in your company's source control there are several copies of the same code. We had dozens of products, so that would have meant dozens of copies. Hunt them down and kill them.

  2. We had a small team of 10-12 developers dedicated to maintaining the core library and its test suites. We were also responsible for fielding calls from the other 1100 developers in the company about how to use the core library, so as you can imagine, we were very busy.

  3. Each other project needs to work with the version of the core library that it is known to work with. You can use version control branches to test new releases of the core library with old products to make sure you don't break code that works. If the core team does a thorough job of testing, this should go very smoothly. The only time this ever got really complicated for us was when the core API changed, or when we flat out screwed something up. Even if you're very confident in your core testing, use branches to test individual products.

Bill the Lizard
+1  A: 

Create an Anti-corruption (DDD) layer for the existing library... this is nothing but a facade.. and then write unit-test for this anti-corruption layer... Now even if someone upgrade/update the library you would know if something is broken by running the unit tests...

These tests could also serve as documentation of contract... and not every project that need to use the library has to write this anti -corruption layer, if they are using the same exact functionality..

StackUnderflow
+2  A: 

I agree - this is difficult. In our small team (consulting .. not a product company - which made it harder), we had one common component that stood out from the others. In this case the recipe for success was:

  • Make a good developer responsible for developing the component
  • Make a good developer the gatekeeper for maintaining the component
  • Make sure all upgrades (there were several) are backward compatible
  • Make sure there is some basic documentation (or a simple reference application) explaining how the component is to be used
  • Make sure all developers know that the component exists (!) and where they can find it (along with the code, if they wish to review it)

Give developers the ability to review the code and suggest better implementations or refectoring, but have the final mods go through an experienced gatekeeper. When the component were upgraded, older apps did not have to upgrade. If we did a new release, we evaluated if we wanted to upgrade, and if we did, all we needed to do was swap the libraries - no code needed to change, unless we wanted to use some new features available through the upgrade. Resistance is inevitable, but sometimes it is a good sort of resistance when it comes from good developers who have better ideas for a new generation or refactored component.

Sliceoftime
+1  A: 

"Duplication is the root of all evil"

Sounds to me like you need:

  • An artifact repository like Ivy so you can have the libraries shared and versioned with a distinction between versions that are API stable and ones that are "maturing"
  • Tests for the libraries
  • Tests for the projects using them
  • A continuous integration system so that when an incompatibility or bug is introduced both the project and the original library developer are notified
Jeffrey Fredrick
A: 

Duplication is the root of all evil

I would argue that unchecked government is the root of all evil :)

I do get a lot of flack for even suggesting that duplication should be an option. I understand why, but let me complicate this a bit.

Say you have a fairly large library that doesn't actually do anything in particular - it's just a collection of utilities. There are NO tests for this library - at all. You need only one function from it. Say, something that parses out a file's extension.

Pop quiz: do you just write something as small as this in your own project, or you bite the bullet and use the free-for-all untested set of utilities, which WILL break your application if someone breaks the function?

Also, imagine you are in environment where writing tests is not part of the culture, since most projects are very intense and have a very short development span.

Duplicating large systems - such as client registration - would be dumb beyond belief, of course. However, aren't there any cases where it is safer to duplicate something fairly small in your project if the alternative is not safe enough (no system for maintaining common code).

Think of it this way - and this happens all the time - multiple contractors working on different projects, for the same company. They don't even know about each other.

My argument is this:

If a team cannot dedicate to maintaining a solid common codebase, or if the environment does not give them enough time to, it's best to let them work as separate "contractors".

You will STILL need to use large existing systems that simply cannot be duplicated.

Andrei Taranchenko
Yes, if the common code has no test suite, and any changes to the common code have uncontrolled roll-out to other projects, then I can see why duplication would happen as the least-effort defence in this scenario. But duplication is the worst of all the available solutions to this situation. The other answers give much better solutions.
Craig McQueen
A: 

Duplicating large systems - such as client registration - would be dumb beyond belief,

That's why those systems publish external interfaces.

If you define a library as shared code between projects: in my experience that's almost always bad. A project should be stand alone, and updates for one project should not affect other projects.

Even if you start with libraries, you'll end up duplicating code anyway. Want to hotfix project 1? It was released with library 1.34, so to keep the hotfix as small as possible, you'll go back to library 1.34 and fix that. But hey-- now you did exactly waht the library was supposed to avoid-- you've duplicated the code.

Every developer uses Google to find code and copy it into his application. That's probably how they found Stack Overflow in the first place. Imagine what would happen if Stackoverflow published libraries instead of code snippets, and you'll get an idea of the problems that afflicts many well meaning library creators.

Libraries tend to be generic solutions to specific problems. Typically, the generic solution is more complex than the sum of the two specific solutions. This means you need one good programmer to solve a problem that could have been solved by two morons. Sounds like a bad tradeoff to me :D

Andomar
Even though the library is more complex, it's better tested. So in almost all cases, I'd prefer the well-tested library than the poorly-tested specific solutions.
Craig McQueen
If you have products A, B, and C, which all depend on librar L. Now you release a new version of A with fixes in L. Will you test both B and C thoroughly? Really?
Andomar
A: 
Asaf Mesika