I am an indie developer working on multiple Android projects. I am having problem with maintaining same functionality on different projects. For example, three of my apps use the same 2 classes; since they are different projects, when I need to make a change in those classes, I need to make it three times. Is there a simple solution to this kind of common problem?
Separate the shared code off into a library, and include the library in the projects.
A side effect is that this will help maintain separation of concerns by enforcing modularity. The cost is that it may take some work to separate; the benefit is better future maintainability. Also, if you ever decide to, you can release the library separately (either commercially or as open source) from your applications.
Put your projects under version control, using a dvcs like git or mercurial etc. Put the shared code in a submodule in git. Use the submodule in the projects that need it. When you update the submodule, can pull the changes to other projects with a single command.
No one else mentioned the double-edged sword yet, so I'll add my 2 cents. If you have multiple projects and they all share some reusable code, according to good programming practices/principles (DRY for example), you should place the code in one global place and structure it in such a way so that it can be shared by all your projects without any modifications. In other words, define interfaces to be generic and common enough to suit everyone.
There are few options for doing this: 1) Create a base project that others depend on. This project can create a binary distribution that other projects consume. 2) Pull an open-source model within your organization. Have common code be its own code branch and have other projects pull in code same way you would take source code from any OSS online.
Now here's where the sword comes in...
Placing code into a common place that other projects/people depend on can become rather expensive. Let's say you have a piece of code and 4 other projects depend on it. One of your customers that uses project A finds a bug and you have to make a fix. You rush the fix out and that customer is happy. But you just modified some code that 3 other projects depend on. Did you retest all of those to make sure no edge conditions were introduced?
Common code must also be crafted much more carefully and module interfaces must be much better designed because that code must accommodate not one but 4 clients and each of those might just use this code with ever so slight of a difference.
If your projects are on different release cycles, you need to be even more careful about managing the common code. You can't simply make changes in the common code because project B needs new functionality, if you are 3 days away from cutting the final image for project C.
I'm not saying common library isn't the right solution. I'm a strong supporter of DRY and I've created and supported common code before and continue to do so. Just wanted to put it out there that simply making code common will have it's own expenses.
If you are the only one reusing this code, this isn't as bad. If you have a team of engineers and they start using the common code, expenses shoot up even more. If others are involved, expect placing code into a common library to take 3 times as much time as it would takes to get it to a point where you think it is "complete". You will need to a) make the library more robust to protect against all kinds of edge conditions and invalid usage, b) provide documentation so others can use the library and c) help other people debug when they use the library in a way that you haven't anticipated and your documentation didn't cover their specific use case.
Few suggestions I can offer:
- Having common code covered by automated unit tests can go a long way and give you some piece of mind that when you make a change, you didn't just break some other project.
- I would only place very stable functionality into common code. If you wrote a class last year to do timer management and you haven't changed it in 9 months and now you have 3 other projects that need timers, then sure that's a good candidate. But if you just wrote something last week, well... you don't have that many options (and I hate copy/pasting code probably more than the next guy) but I'd think twice about making it common.
- If piece of code is trivial but you've used it in several places, maybe it's better to bite the bullet, leave DRY alone and let multiple copies live.
- Sometimes it pays to not simply put common code into a common location and have everyone reference it. But rather treat common code as its own releasable with versions, release dates and everything. This way project C can says, "I use common library v1.3" and if project D needs new functionality, you leave v1.3 alone, release v1.4 and project C doesn't get affected. Keep in mind, it is MUCH, MUCH more expensive to treat common code as its own product rather than simply having it checked into a common location.
This is an idealized solution, and may take some effort to get working.
The DRY principle states that there should be a single authoritative source of truth. This dictates the use of a single source repository for all program logic, with no duplication; files organized to promote sharing and reuse of documents.
The pragmatic requirements of communication in a distributed, multi-team environment suggests that there should be multiple independent repositories, one for each project or collaboration.
I would approach this problem by submitting to the inevitable: you will need to take both approaches concurrently, and we will use scripting & automation to smooth over the fact that the approaches are contradictory.
The one unified repository will be your single authoritative source. The build process for each project will copy all the files used by that project (and only those files) into an intermediate location, then build from that intermediate location. (Unison or some similar tool can be used to move deltas instead of whole files).
These intermediate locations can be used as local working copies for the set of secondary, derived, or downstream repositories. Post-commit hook scripts on the authoritative repository will update all of the intermediate locations, and, for each in turn, check if it has changed, and make the same commit to the corresponding secondary repository if a change is detected.
This way, the multiple secondary repositories are kept in sync with the single authoritative source repository, and the build process ensures that the secondary repositories contain all of the (possibly shared) documents and other files that are needed for the build to succeed. Finally, and most importantly, the development and build process ensures that files are edited in one place and one place only, and that all copies are kept consistent and up to date.