ModeShape is a community of developers and users that are trying to build the most useful and capable JCR implementation. Over the 3 years that we've been around, our community has evolved and we've figured out what works and what doesn't work for us. This section attempts to capture some of these practices. This is not a complete list, but hopefully it gives you some idea about how we do what we do.
Release Early, Release Often
Open source software changes quickly. We find and fix bugs, add new features, and evolve the software to do what our community needs and wants. And so one of our goals is to release about every 4-6 weeks so that our users don't have to wait long before they can see and use the changes and improvements.
Of course, this doesn't always happen. Sometimes we get bogged down with bigger features or a lot of fixes. Sometimes we slip our releases so that we can incorporate something we feel is important. Or sometimes we get busy with holidays and vacations and other work. But we do our best.
Keep it Simple
This one is probably obvious. It's a lot easier to work on and maintain simpler code than it is complex code, so we try to make things as simple as possible, but not simpler. This is difficult to achieve, and there are times when a complexity is warranted. After all, JCR repositories are complex systems with a lot of specific behavior, and ModeShape provides various ways of integrating and federating content.
We try to use our issue tracking system (JIRA) to track all bugs, feature requests, and tasks. Generally issues are targeted to a release (or a 'future release' catch all), and developers assign themselves to issues they will be working on. As a developer works the issue, they add comments describing what they've done, possible approaches they've considered, and even when they seek advice from the community. All these changes are automatically emailed to the issues mailing list, so the rest of the community can easily follow along with what everyone is working on.
Whenever possible, the title, description and comments on JIRA issues should be clear and readable, and should use proper grammar and spelling. Try to avoid acronyms, unless they're used frequently or are commonly understood. If you've gotten a stack trace, include it in the issue. But since JIRA descriptions are included in every email notification, so put the messy and long stack traces in comments rather than the description. When it doubt, provide more information than you think is necessary.
If you're going to make changes to the code, please do it as part of a JIRA issue. If there is no JIRA issue for a change you want to make, then create one. This helps us know who is working on what, and we use JIRA to automatically build the release notes for each release.
We believe in testing, because it's the only way that we can be confident the software does what it's supposed to do, and that it does what we expect even after we make changes. We don't really care if you write tests first, last, or somewhere in between. But we do expect that the code can be and is tested. And we expect that developers do run at least the unit tests prior to committing changes, though we'd prefer that developers run a full build before each commit.
We use unit tests, integration tests, and system tests. But since we use JUnit for our testing (and Hamcrest assertions), we don't really distinguish too much between the different kinds of tests. Ideally, every class would have a corresponding unit test class that runs very quickly, and we sometimes use mock objects to stub out components that these unit tests are not focusing on. Integration tests generally take longer to run and require a lot of real components, but we still use JUnit for these. One problem with mocks, however, is that you're not testing how a component interacts with other components. While this is sometimes useful, it's often far more pragmatic to write integration tests that start and use a repository. (See this test for an example.)
An important aspect of tests is that they can be run over and over at any time, and each time the tests verify that the system behaves as expected and that bugs don't reappear. So we run our tests continuously to make sure that the software is always in a buildable and runnable state, and that recent changes haven't cause a regression in behavior.
Now, having said this, we also are very pragmatic when it comes to writing tests and how much of our code is covered by our tests. Ideally, we'd have 100% coverage, but that's simply not practical. (We'd be spending almost all of our time writing tests!) Instead, each developer is responsible for making the changes to the software while being very confident that these changes will do what he or she expects (and no more). Most of the time, the only way to have this confidence is to write new tests or adapt existing tests.
We also encourage developers to test smarter. Many of us have found that we can write more useful and effective tests by first stubbing out multiple test methods with names that describe what the component should and should not do, and <emphasis>then</emphasis> filling in those methods to verify that the component exhibits that behavior. We'll often words that are meaningful to users or observers of the component, and will use "should" and "should not" in our test method names. For example, if a method in a component should not throw an exception if a stream parameter is null, when we'd write a test method called <code>shouldNotThrowAnErrorWhenStreamIsNull()</code>. This style of writing tests is based upon Behavior-driven development (BDD), and we've found it helps use quickly identify what needs to be tested.
Comments are pretty important for any software activity that involves more than one person. We do expect that methods and classes have full JavaDoc comments, and the code should contain concise but meaningful comments any place where it is not immediately obvious from the code. JIRA comments should be clear and readable.
Each commit should leave the software in a fully buildable and runnable state with all unit and integration tests passing. Sure, things happen and every developer will make a commit that breaks something. All we ask is that you try your best. Just remember that everyone will find out when you break the build. (Peer pressure can be a good thing.)
Also, try to make each commit a logically separate changeset. If you can, make each commit easily digestable - don't code for a week before you commit one massive set of changes. Don't leave the code poorly formatted or with unused imports; keep things as clean (or cleaner) than you found them. And always get the latest changes before committing, otherwise your changes may not compile.
Every commit should include a useful and readable commit message, so get in the habit of writing good commit messages. Each commit message should include the JIRA issue(s) under which the changeset was made; there should almost always be at least one JIRA issue! Imperative present tense is preferred (e.g., "Added tests for ..." or "Changed the cache mechanism so that ..."). And then be sure to update the JIRA issues with the same (or similar) comment.
Here's a template for a good commit message, based upon one written by Tim Pope:
MODE-123, MODE-234 Short (50 chars or less) summary of changes More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here - Use a hanging indent
Good commit messages make it easier for everyone to understand what changes were made without having to dive through the history. And tying your commits back to JIRA provide more context for the commit, possibly tying multiple commits and JIRA issues together.