Code style and quality guide
1 Pull Requests & Changes Rule
ISSUE
/PR
(pull request) driving and namingAfter creating a new
PR
, you need to associate the existing correspondingISSUE
at the Github Development button on thePR
page (if there is no corresponding ISSUE, it is recommended to create a new corresponding ISSUE).Title naming format
[feature/bugfix/doc/improve/refactor/bug/cleanup] title
Description
- Please fill in the
PR
template to describe the contribution. So that the reviewer can understand the problem and solution from the description, rather than just from the code. - Check the CheckList
- Please fill in the
It's recommended that
PR
should be arranged changes such ascleanup
,Refactor
,improve
, andfeature
into separatedPRs
/Commits
.Commit message(English, lowercase, no special characters)
The commit of messages should follow a pattern similar to the[feature/bugfix/doc/improve/refactor/bug/cleanup] title
2 Code Checkstyle
2.1 Configure Checkstyle
- Install the Checkstyle-IDEA plugin.
- Open Checkstyle Settings. Click Settings → Tools → Checkstyle.
- Set Checkstyle version to 10.14.2.
- Set Scan scope to Only Java sources (including tests).
- Click + button in the Configuration section to open a dialog to choose the checkstyle config file.
- Enter a Description. For example, hertzbeat.
- Select Use a local checkstyle file.
- Set File to script/checkstyle/checkstyle.xml.
- Select Store relative to project location.
- Click Next → Next → Finish.
- Activate the configuration you just added by toggling the corresponding box.
- Click OK.
Backend code specification Maven plugin:
checkstyle
Just runmvn checkstyle:checkstyle
.Frontend code formatting plugin
eslint
Just runnpm run lint:fix
in web-app
3 Programming Specification
3.1 Naming Style
Prioritize selecting nouns for variable naming, it's easier to distinguish between
variables
ormethods
.Cache<String> publicKeyCache;
Pinyin abbreviations are prohibited for variables (excluding nouns such as place names), such as chengdu.
It is recommended to end variable names with a
type
.
For variables of typeCollection/List
, takexxxx
(plural representing multiple elements) or end withxxxList
(specific type).
For variables of typemap
, describe thekey
andvalue
clearly:Map<Long, User> idUserMap;
Map<Long, String> userIdNameMap;That can intuitively know the type and meaning of the variable through its name.
Method names should start with a verb first as follows:void computeVcores(Object parameter1);
Note: It is not necessary to strictly follow this rule in the
Builder
tool class.
3.2 Constant Variables Definition
Redundant strings should be extracted as constants
If a constant has been hardcoded twice or more times, please directly extract it as a constant and change the corresponding reference. In generally, constants in
log
can be ignored to extract.Negative demo:
public static RestResponse success(Object data) {
RestResponse resp = new RestResponse();
resp.put("status", "success");
resp.put("code", ResponseCode.CODE_SUCCESS);
resp.put("data", data);
return resp;
}
public static RestResponse error() {
RestResponse resp = new RestResponse();
resp.put("status", "error");
resp.put("code", ResponseCode.CODE_FAIL);
resp.put("data", null);
return resp;
}Positive demo:
Strings are extracted as constant references.
public static final String STATUS = "status";
public static final String CODE = "code";
public static final String DATA = "data";
public static RestResponse success(Object data) {
RestResponse resp = new RestResponse();
resp.put(STATUS, "success");
resp.put(CODE, ResponseCode.CODE_SUCCESS);
resp.put(DATA, data);
return resp;
}
public static RestResponse error() {
RestResponse resp = new RestResponse();
resp.put(STATUS, "error");
resp.put(CODE, ResponseCode.CODE_FAIL);
resp.put(DATA, null);
return resp;
}
Ensure code readability and intuitiveness
The string in the
annotation
symbol doesn't need to be extracted as constant.The referenced
package
orresource
name doesn't need to be extracted as constant.
Variables that have not been reassigned must also be declared as final types.
About the arrangement order of
constant/variable
linesSort the variable lines in the class in the order of
public static final V
,static final V
,protected static final V
,private static final V
public static v
,static v
,protected static v
,private static v
public v
,v
,protected v
,private v
3.3 Methods Rule
Sort the methods in the class in the order of
public
,protected
,private
Static methods of a class can be placed after non-static methods and sorted according to consistent method visibility.
When there are restrictions on the method, the parameters and returned values of the method need to be annotated with
@Nonnull
or@Nullable
annotations and constraints.For example, if the parameter cannot be null, it is best to add a
Note: that the package name is javax.validation.requirements@Nonnull
annotation. If the returned value can be null, the@Nullable
annotation should be added first.If there are too many lines of code in the method, please have a try on using multiple sub methods at appropriate points to segment the method body.
Generally speaking, it needs to adhere to the following principles:
- Convenient testing
- Good semantics
- Easy to read
In addition, it is also necessary to consider whether the splitting is reasonable in terms of components, logic, abstraction, and other aspects in the scenario.
However, there is currently no clear definition of demo. During the evolution process, we will provide additional examples for developers to have a clearer reference and understanding.
3.4 Collection Rule
For
collection
returned values, unless there are specialconcurrent
(such as thread safety), always return theinterface
, such as:- returns List if use
ArrayList
- returns Map if use
HashMap
- returns Set if use
HashSet
- returns List if use
If there are multiple threads, the following declaration or returned types can be used:
private CurrentHashMap map;
public CurrentHashMap funName();Use
isEmpty()
instead oflength() == 0
orsize() == 0
Negative demo:
if (pathPart.length() == 0) {
return;
}Positive demo:
if (pathPart.isEmpty()) {
return;
}
3.5 Concurrent Processing
The
Note: During the evolution process, we will provide additional examples for developers to have a clearer reference and understanding.thread pool
needs to be managed, using a unified entry point to obtain thethread pool
.Thread pool
needs to be resource constrained to prevent resource leakage caused by improper handling
3.6 Control/Condition Statements
Avoid unreasonable
condition/control
branches order leads to:Multiple code line
depths
ofn+1
Redundant lines
Generally speaking, if a method's code line depth exceeds
2+ Tabs
due to continuous nestedif... else..
, it should be considered to trymerging branches
,inverting branch conditions
extracting private methods
to reduce code line depth and improve readability like follows:
Union or merge the logic into the next level calling
Negative demo:
if (isInsert) {
save(platform);
} else {
updateById(platform);
}Positive demo:
saveOrUpdate(platform);
Merge the conditions
Negative demo:
if (expression1) {
if(expression2) {
// ......
}
}Positive demo:
if (expression1 && expression2) {
// ......
}
Reverse the condition
Negative demo:
public void doSomething() {
// Ignored more deeper block lines
// .....
if (condition1) {
// ...
} else {
// ...
}
}Positive demo:
public void doSomething() {
// Ignored more deeper block lines
// .....
if (!condition1) {
// ...
return;
}
// ...
}
Using a single variable or method to reduce the complex conditional expression
Negative demo:
if (dbType.indexOf("sqlserver") >= 0 || dbType.indexOf("sql server") >= 0) {
// ...
}Positive demo:
if (containsSqlServer(dbType)) {
// ....
}
//.....
// definition of the containsSqlServer
Using
sonarlint
andbetter highlights
to check code depth looks like good in the future.
3.7 Code Comments Rule
Method lacks comments:
When
: When can the method be calledHow
: How to use this method and how to pass parameters, etc.What
: What functions does this method achieveNote
: What should developers pay attention to when calling this method
Missing necessary class header description comments.
Add
What
,Note
, etc. like mentioned in the1
.The method declaration in the interface must be annotated.
If the semantics of the implementation and the annotation content at the interface declaration are inconsistent, the specific implementation method also needs to be rewritten with annotations.
If the semantics of the method implementation are consistent with the annotation content at the interface declaration, it is not recommended to write annotations to avoid duplicate annotations.
The first word in the comment lines need to be capitalized, like
param
lines,return
lines. If a special reference as a subject does not need to be capitalized, special symbols such as quotation marks need to be noted.
3.8 Java Lambdas
Prefer
non-capturing
lambdas (lambdas that do not contain references to the outer scope). Capturing lambdas need to create a new object instance for every call.Non-capturing
lambdas can use the same instance for each invocation.Negative demo:
map.computeIfAbsent(key, x -> key.toLowerCase())
Positive demo:
map.computeIfAbsent(key, k -> k.toLowerCase());
Consider method references instead of inline lambdas
Negative demo:
map.computeIfAbsent(key, k-> Loader.load(k));
Positive demo:
map.computeIfAbsent(key, Loader::load);
3.9 Java Streams
Avoid Java Streams in any performance critical code.
The main motivation to use Java Streams would be to improve code readability. As such, they can be a good match in parts of the code that are not data-intensive, but deal with coordination.
Even in the latter case, try to limit the scope to a method, or a few private methods within an internal class.
3.10 Pre-Conditions Checking
- Use a unified
Utils.requireXXX
to complete the validation of the prerequisite, and if possible, replace theAlertXXException.throwIfXXX
by new pre-conditions checking.
3.11 StringUtils
Use
StringUtils.isBlank
instead ofStringUtils.isEmpty
Negative demo:
if (StringUtils.isEmpty(name)) {
return;
}Positive demo:
if (StringUtils.isBlank(name)) {
return;
}
Use
StringUtils.isNotBlank
instead ofStringUtils.isNotEmpty
Negative demo:
if (StringUtils.isNotEmpty(name)) {
return;
}Positive demo:
if (StringUtils.isNotBlank(name)) {
return;
}
Use
StringUtils.isAllBlank
instead ofStringUtils.isAllEmpty
Negative demo:
if (StringUtils.isAllEmpty(name, age)) {
return;
}Positive demo:
if (StringUtils.isAllBlank(name, age)) {
return;
}
3.12 Enum
Class
Enumeration value comparison
Negative demo:
if (status.equals(JobStatus.RUNNING)) {
return;
}Positive demo:
if (status == JobStatus.RUNNING) {
return;
}
Enumeration classes do not need to implement Serializable
Negative demo:
public enum JobStatus implements Serializable {
...
}Positive demo:
public enum JobStatus {
...
}
Use
Enum.name()
instead ofEnum.toString()
Negative demo:
System.out.println(JobStatus.RUNNING.toString());
Positive demo:
System.out.println(JobStatus.RUNNING.name());
Enumeration class names uniformly use the Enum suffix
Negative demo:
public enum JobStatus {
// ...
}Positive demo:
public enum JobStatusEnum {
// ...
}
3.13 Deprecated
Annotation
- Negative demo:
@deprecated
public void process(String input) {
// ...
}
- Positive demo:
@Deprecated
public void process(String input) {
// ...
}
4 Log
Use
placeholders
for log output:Negative demo
log.info("Deploy cluster request " + deployRequest);
Positive demo
log.info("load plugin:{} to {}", file.getName(), appPlugins);
Pay attention to the selection of
log level
when printing logsWhen printing the log content, if the actual parameters of the log placeholder are passed, it is necessary to avoid premature evaluation to avoid unnecessary evaluation caused by the log level.
Negative demo:
Assuming the current log level is
INFO
:// ignored declaration lines.
List<User> userList = getUsersByBatch(1000);
LOG.debug("All users: {}", getAllUserIds(userList));Positive demo:
In this case, we should determine the log level in advance before making actual log calls as follows:
// ignored declaration lines.
List<User> userList = getUsersByBatch(1000);
if (LOG.isDebugEnabled()) {
LOG.debug("All ids of users: {}", getAllIDsOfUsers(userList));
}
5 Testing
It's recommended to use
JUnit5
to develop test case preparationThe implemented interface needs to write the
e2e
test case script under thee2e
module.