Skip to content

Team Practices

Phillip Webb edited this page May 24, 2021 · 15 revisions

As a team we are constantly refining the practices that we use to manage the Spring Boot project. This document provides an overview the most important ones.

If you’re an external contributor, you might find this document interesting, but you probably want to read Working with the Code instead.

Team Principles

The overriding principal adopted by the team is that it should be possible for any member to work on any aspect of the project. Most of the processes and practices that we have implemented are driven by this vision.

We believe it’s important that no single individual ever owns a specific part of the project. We don’t want a single point of failure. As much as possible, we try to automate things away.

Overview

Inclusivity

We strive to make Spring Boot an inclusive project and include a code of conduct that we enforce. Whenever possible we avoid oppressive language and follow IEFT recommendations. This extends to the issue tracker where will routinely edit issue descriptions, for example, replacing "Hi Guys" with "Hi Team".

Versioning

It’s not really possible for Spring Boot to use semantic versioning since every release would have to be a major. Instead we try to use the version number as an indicator of the amount of pain that an upgrade will cause.

The version number format used by Spring Boot is <major>.<minor>.<patch>(-<qualifier>).

Patch Releases

Patch releases should be API compatible drop-in replacements for users. They should only contain bug fixes.

Minor Versions

Minor versions contain new features. It should generally be possible to upgrade from a previous minor release without too much effort as long as the major version has not changes and no deprecated methods are being used.

Major Versions

Major versions are reserved for large breaking changes. It’s expected that users will need to do some work when upgrading major versions.

Upgrade Empathy

It’s important to remember that upgrades are not an enjoyable task for most users. As a team we should always provide as much help as possible and have empathy for our users.

We should always carefully consider the reasons behind any change that we make. It’s sometimes better for us to take on the pain that we’d otherwise inflict on our users. For example, we often choose to support older versions of Java even though we’d like to use newer language features ourselves.

Deprecation Policy

Deprecated classes and methods must be annotated with @Deprecated and include a @deprecated javadoc tag. The tag should be of the form @deprecated since <version> for removal in <version> in favor of <replacement>.

See this page for more details about our deprecation policy.

New Features vs Bugs

New features should target the upcoming minor release and should not be included in patch releases. Bugs should be targeted to the oldest supported release, unless they are particularly risky or hard to fix in an older branch.

Third-party Dependency Upgrades

The upgrade policy for third-party dependencies is documented here. We use a tool called Bomr to upgrade dependencies.

Minor versions will always try to move to the latest version of a third-party dependency.

Using SNAPSHOT Dependencies

We try to move dependencies on other Spring projects to their SNAPSHOT versions about a week or two before we release. This helps us identify issues early to fix them, but not suffer too much instability in our own build.

The smoke tests are particularly helpful in ensuring that all Spring dependencies work well together.

Release Cadence

Spring Boot releases minor versions every 6 months. We release on the 3rd Thursday of the month.

Details of the schedule are availble here.

Managing Issues and Pull Requests

Spring Boot is a popular project and as a result it has a lot of issues raised against it. In order to deal with the volume of issues, we have had to put in several processes to help with issue managed management.

Dealing With Issues

Out issue process is documented in this wiki page. It covers the triage process, the labels we use and the milestone that have.

Forward Port Issues

In order to generate release notes we add status: forward-port

Canned Responses

We used GitHub’s "saved replies" feature to quickly provide common respones in issues. For example, it’s common for us to close issues that are questions rather than bugs.

A list of our saved replies are available here.

First Timers Only

Every now and then we like to try and find an issue that is suitable for someone that hasn’t contributed to Spring Boot before. Our aim is to encourage people that wouldn’t usually contribute to open source to give it a go.

First timer issues are labeled for: first-timers-only and usually have a much more detailed description than regular issues. The first person to claim the issue is assigned and we then work with them so that they can contribute a pull-request.

Pull Requests

Spring Boot has a vibrant community of contributors the regularly send pull requests. We triage pull requests in the same way as issues and will assign them to a specific milestone.

Once we assign a pull request to a milestone, any related issue is labeled as status: superseded and closed.

Details on how to merge a pull request are documented here.

Git Practices

Commit Messages

Commit messages are a vital tool when trying to debug a regression. Spring Boot follows these 7 rules for commit messages:

  1. Separate subject from body with a blank line

  2. Limit the subject line to 50 characters

  3. Capitalize the subject line

  4. Do not end the subject line with a period

  5. Use the imperative mood in the subject line

  6. Wrap the body at 72 characters

  7. Use the body to explain what and why vs. how

In addition, almost all commit messages include a reference to a GitHub issue or pull request. We reference issues using See, Fixes or Closes then gh-NNNN.

For example:

Allow optional ConfigDataLocationResolver results

Update `ConfigData` so that it signal if is considered optional. This
update allows `ConfigDataLocationResolvers` to return results that
behave in the same way as `optional:` prefixed locations without the
user themselves needing to prefix the location string.

Closes gh-25894

The following blogs are worth reading on the subject:

Maintenance Branches

The main branch of the Spring Boot repository contains the latest version of Spring Boot. This is usually the next minor or major version, but it may also be the next patch release if maintenance branches have not yet been created.

We keep maintenance branches in form <major>.<minor>.x for all active versions. For example, if main is for an upcoming 2.3.0 version, we’d have 2.2.x and 2.1.x maintenance branches.

Spring Boot uses forward-merges to apply fixes from maintenance branches forward. Please read Working with Git Branches for more details.

Code

As much as possible we try to make the Spring Boot codebase appear as if it were written in a single voice. To do this, we rely on tools such as checkstyle and the spring-javaformat code formatter. We also regularly "polish" code to improve it and bring consistency.

We hope that by having a consistent code style, it will be easier for contributors and the team to work on difference areas of the codebase.

IDEs

We want to ensure that Spring Boot works well with any IDE but we’re especially invested in Eclipse and IntelliJ. See Working with the Code for detailed instructions on how to setup your IDE.

Formatting

Our codebase is automatically formatted using the spring-javaformat project. Whilst there are pros and cons to auto-formatting, we’ve found it to be beneficial.

There, however, are still a few things that need manual teeaks:

Fluent APIs

Fluient APIs are often very hard to read when auto-formatted. To fix this, you can try moving to a one-statement-per-line style rather than chaining calls.

Some fluent APIs also offer lambda callback variants which will look much better.

If those tips don’t work, using @formatter: off/@formatter: on can be used as a last resort.

Whitespace

For formatter will not touch whitespace in a method body, but we generally like to remove it. Removing body whitespace helps to make it easier to see where methods begin and end.

If you have method body that is becoming long and hard to read, you might want to extract some private methods from it.

Note

Other Spring projects have different opinions on whitespace. Spring Framework has additional lines around constructors and when method signatures wrap. Spring Data uses whitespace within the method body.

Checkstyle

We use checkstyle to bing as much consistency as possible to our codebase and catch potential programming issue early. We will add new rules to the spring-javaformat project if we find things that we enforce.

Although checkstyle can be pain sometimes, we found it helpful overall and it’s especially useful for contributors.

Naming Classes/Methods

Naming classes and methods is something that cannot be automated so we generally rely on team review. We try to follow the conventions already established by the Spring Framework.

Class and method names in Spring Boot can be quite long, especially for classes that are more internal.

Polish Commits

Every member of the team is encouraged to make "Polish" commits whenvever they find code that could be improved. A polish commit is a commit that makes the code easier to read or reason about, but does not change functionality.

Testing

Spring Boot relies heavily on tests to ensure that code works as expected. Test are written using JUnit 5 and assertions are made using AssertJ. We use Mockito for mocking.

Test Organization

Mostly there’s a 1-1 mapping between a class and its unit tests. For example, SpringApplication.java will have a related SpringApplicationTests.java file.

The top comment of the *Tests file has comment so that it’s easy to ctrl + click back to the class being tested:

/**
 * Tests for {@link SpringApplication}.
 *
 * @author ...
 */
class SpringApplicationTests {

  ...

}

Sometimes we need to test the interactions between several classes. In these situations, we’ll use +*IntegrationTests as the test suffix.

We also have "smoke test" projects that we use for high level integration tests.

Test Method Naming

We like to use BDD (behavior driven development) style method names whevener possible. These are of the form: <method-under-test>[when<condition>]<expecation>.

For example: readLoadsFile might test that the read method loads file contents. Or readWhenFileIsEmptyThrowsException might check that the read method throws an exception if the file is empty.

Note
Over the years we’ve refined out test naming conventions so things aren’t as consistent as we’d like. Polish commits can be used if you find an older set of tests that need migrating.

Javadoc

Javadoc is a very important part of the Spring Boot API. All classes and all public methods should have high quality documentation.

Advice that code should be "self documenting" is really applicable for our code so we tend have quite a lot of duplication in our javadoc. For example, a method javadoc may include a "Returns …​" commend and have a @returns tag as well.

Often writing javadoc is a good way to verify that a class or method is named correctly. If you find yourself writing different terms in the javadoc, then perhaps the code should be renamed.

Author Tags

We like to include @author tags in order to give recognition to contributors. Although this is also available in git, it’s nice to have the @author tag as well since it appears when browsing jar contents in an IDE.

Since Tags

We add @since tags to public classes and public methods to help users know when something was added. The tags are not required for package-private classes.

ConfigurationProperties

Javadoc for @ConfigurationProperties is a little unusual because we document that fields rather than the getters/setters. This allows the comments to be saved in the mata-data json file.

The field javadoc must also be plain text. Don’t use html markup or {@link …​} items.

Documentation

Wiki Documentations Doc Samples Asciidoctor Formatting Generated Asciidoctor Snippets Extensions

Team Communication

Team Calls

CI

CI repo.spring.io

Releases

Release Process Release Notes Release Checklist Changelog Release Process

Other

Regressions CVE Processes Gradle Build

Clone this wiki locally