Development Guidelines
Li Wei
Development Guidelines
Preface
Excerpted from:
Development template reference:
Code Quality
Evaluation criteria:
1. Cyclomatic Complexity
Cyclomatic complexity measures the logical complexity of code; it reflects the number of independent execution paths in a function or method (determined by branches, loops, logical decisions, etc.).
Formula: [ ]
- E – the number of edges in the control‑flow graph (code paths such as
if/elsebranches, loops, method calls, etc.). - N – the number of nodes (statements or conditions in the code).
- P – the number of independent code blocks (usually 1).
Simplification rules: each control structure adds to the cyclomatic complexity, for example:
if/else= +1for/while/do-while= +1- each branch in a
casecondition (switch) = +1 &&/||logical operators = +1
Example of actual complexity calculation:
- Nodes (N):
3conditional statements (if, else if, else) plus tworeturnstatements. - Edges (E): the code paths split into
3branches – executingif, executingelse if, executingelse. - Cyclomatic complexity:
E - N + 2P=4 - 5 + 2 = 3.
Practical standards:
- Ideal value: a single function/method should have complexity ≤ 10; higher values signal that refactoring is needed.
- Management tactics:
- Split large methods into smaller ones.
- Use the Strategy or State pattern to reduce conditional branching.
2. Test Coverage
What it is
Unit‑test coverage measures how much of the application code is exercised by the test suite. It is expressed as a percentage; the higher the percentage, the more thorough the testing.
Calculation
[ ]
Coverage types (increasing precision):
- Statement Coverage: how many lines of code are executed.
- Branch Coverage: whether each branch (e.g., each path of
if-else) is tested. - Condition Coverage: whether all combinations of condition expressions are exercised.
- Path Coverage: whether every possible execution path has been tested.
Tool support
- Java:
JaCoCo,EclEmmaplugins can generate coverage reports showing the proportion of code that is covered.
Practical standards
- Recommended coverage:
- Core business code: ≥ 80 %.
- Non‑core code: at least 60 %–70 %.
- When blind spots are discovered, add tests to cover them.
- Analyzing tool results:
- Inspect uncovered critical methods or logic, especially branches and exception‑handling paths.
3. Dependencies
Dependencies refer to how much a code module/component relies on other modules/libraries/services, reflecting coupling. High coupling makes modules hard to modify and reduces reusability.
How to measure:
- Direct dependencies: the number of other classes, packages, or services a module interacts with directly. Tools can count how many other classes each class references.
- Dependency depth: the number of levels through which a module indirectly depends on other classes.
- Afferent Couplings (Ca): the total number of other modules/classes that use the classes in the current module.
- Impact of high Ca: any change to this module affects many other modules.
- Efferent Couplings (Ce): the number of external modules the current module depends on.
- Impact of high Ce: the module heavily relies on many external modules, making changes more complex.
Practical standards:
- Keep external dependencies (Ce) within reasonable bounds, e.g.:
- Core modules: Ce ≤ 5 recommended.
- Highly coupled modules (such as a service gateway) should redesign their dependencies.
- Use tools:
- The dependency‑analysis feature in
SonarQubeto locate highly coupled classes/modules. - Java’s
JDependtool can quantify class dependency relationships.
- The dependency‑analysis feature in
4. Duplication Rate
Duplication rate measures the proportion of repeated logic in the codebase. When identical or similar implementations appear across files, methods, or blocks, they are considered “duplicate code,” which harms reuse and maintainability.
Calculation: [ ]
- Duplicate blocks are identified based on repetition; typically, a sequence of 3 or more identical lines is treated as a duplicate block.
- Tools such as
SonarQube,CheckStyledetect the locations and extents of duplicated code.
Typical case:
After optimization:
… (content omitted)
Practical standards:
- Recommended values:
- ≤ 5 % duplication is excellent.
- 5 %–10 % is acceptable but should be optimized.
10 % increases maintenance cost and warrants refactoring.
- Refactoring approaches:
- Extract common methods.
- Apply Template, Factory, or similar patterns to reduce repeated logic.
- Consolidate utility classes to reuse common functionality.
Programming Conventions
Naming Style
- Class names use UpperCamelCase, except for suffixes such as DO / PO / DTO / BO / VO / UID, etc.
- Good example:
blockList,allowList,secondary
- Good example:
- Method names, parameter names, member variables, and local variables all use lowerCamelCase.
- Good example:
ForceCode,UserDO,HtmlDTO,XmlService,TcpUdpDeal,TaPromotion
- Good example:
- Constants are all uppercase with underscores separating words, aiming for clear, complete semantics.
- Good example:
MAX_STOCK_COUNT,CACHE_EXPIRED_TIME
- Good example:
- Abstract classes start with
AbstractorBase; exception classes end withException; test classes start with the name of the class they test and end withTest. - In POJO classes, do not prefix boolean fields with
is, because some frameworks may misinterpret the getter and cause serialization errors.- Note: In MySQL naming conventions (rule 1), boolean variables use
is_xxx; a mapping fromis_xxxtoxxxmust be established. - Bad example: a Boolean field
isDeletedwhose getter is alsoisDeleted(). Some frameworks think the underlying field isdeleted, causing missing data or exceptions.
- Note: In MySQL naming conventions (rule 1), boolean variables use
- Package names are all lowercase, with a single natural‑language English word between dots. Packages use singular nouns, but class names may be plural when appropriate.
- Good example: utility package
com.alibaba.ei.kunlun.aap.util; classMessageUtils
- Good example: utility package
- Avoid using identical names for member variables in a superclass and subclass, or for local variables in different blocks of the same method.
- Explanation: Same‑named fields in a subclass hide the superclass fields (even if
public), and identical local variable names in separate blocks are legal but confusing. Also avoid using the same name for a non‑setter/getter parameter as a member variable.
- Explanation: Same‑named fields in a subclass hide the superclass fields (even if
- Do not use obscure English abbreviations that are hard to understand.
- Bad examples:
AbsClassforAbstractClass,condiforcondition,FuforFunction. Such shortcuts severely reduce readability.
- Bad examples:
- When naming constants and variables, place the type‑indicating noun at the end to improve recognizability.
- Good example:
startTime,workQueue,nameList,TERMINATED_THREAD_COUNT
- Good example:
- If a module, interface, class, or method implements a design pattern, reflect that pattern in the name to help readers grasp the architecture quickly.
- Good example:
public class OrderFactory; public class LoginProxy; public class ResourceObserver;
- Good example:
- Interface methods and fields must not carry any modifiers (omit
public). Keep interfaces concise and add proper Javadoc. Avoid defining constants in interfaces; if necessary, ensure they are truly fundamental to the whole application.- Good example: interface method signature
void commit();and a base constantString COMPANY = "alibaba"; - Bad example:
public abstract void commit();
- Good example: interface method signature
- Naming rules for interfaces and their implementations:
- Mandatory: For Service and DAO layers, expose a service as an interface; the implementation class uses the
Implsuffix.- Good example:
CacheServiceImplimplementsCacheService.
- Good example:
- Recommended: If the interface describes a capability, use an adjective ending in
‑ableas the interface name.- Good example:
AbstractTranslatorimplementsTranslatable.
- Good example:
- Mandatory: For Service and DAO layers, expose a service as an interface; the implementation class uses the
- Enum class names should end with
Enum; enum constants are all uppercase with underscores.- Note: Enums are special constant classes whose constructors are implicitly private.
- Good example:
ProcessStatusEnumwith membersSUCCESS,UNKNOWN_REASON
- Naming for various processors:
- Manager: a high‑level component that coordinates lower‑level processors or components.
- Executor: executes a specific task, often used for asynchronous jobs or thread‑pool work.
- Handler: a generic request or event processor.
- Processor: transforms or processes data.
- Worker: performs a specific job, typically in concurrent or multithreaded contexts.
- Layer‑specific naming conventions:
- Service / DAO methods:
- Single‑object retrieval: prefix with
get. - Multiple‑object retrieval: prefix with
listand use a plural name, e.g.,listObjects. - Count queries: prefix with
count. - Insertions: prefix with
save/insert. - Deletions: prefix with
remove/delete. - Updates: prefix with
update.
- Single‑object retrieval: prefix with
- Domain model naming:
- Data Object:
xxxDO(wherexxxis the table name). - Data Transfer Object:
xxxDTO(business‑domain name). - View Object:
xxxVO(usually the page name). - POJO is the collective term for DO/DTO/BO/VO; do not name anything
xxxPOJO.
- Data Object:
- Service / DAO methods:
Constant Definition
- No “magic numbers” (undeclared literals) may appear directly in code.
- Floating‑point literals must use uppercase
DorFsuffixes.- Good example:
public static final double HEIGHT = 175.5D;public static final float WEIGHT = 150.3F;
- Good example:
- Do not keep all constants in a single “god class”; group them by functional area.
- Explanation: A monolithic constant class becomes chaotic; you need search to locate a constant, which harms understanding and maintenance.
- Good example: cache‑related constants in
CacheConsts; system‑configuration constants inSystemConfigConsts.
- Constant reuse hierarchy (five levels): cross‑application shared, application‑internal shared, sub‑project shared, package‑level shared, class‑level shared.
- Cross‑application shared: placed in a second‑party library, typically under
client.jar→constantdirectory. - Application‑internal shared: placed in a first‑party library, usually under a module’s
constantdirectory. - Bad example: two developers define “yes” differently in separate classes:
- Class A:
public static final String YES = "yes"; - Class B:
public static final String YES = "y"; A.YES.equals(B.YES)is expected to betruebut returnsfalse, causing production issues.
- Class A:
- Sub‑project shared: constants under the current sub‑project’s
constantdirectory. - Package shared: constants under a
constantdirectory within the package. - Class shared:
private static finalconstants defined directly inside the class.
- Cross‑application shared: placed in a second‑party library, typically under
- If a variable’s value only varies within a fixed set, define it as an
enum.- Explanation: When additional attributes (e.g., a numeric code representing the season of the year) are needed, an enum is appropriate.
Code Formatting
- Limit a line to 120 characters; longer lines must be broken according to these rules:
- The second line is indented 4 spaces relative to the first; from the third line onward, no further indentation is added (see example).
- Operators stay with the following line.
- The dot in a method call stays with the subsequent line.
- When breaking a method‑call argument list, place the line break after the comma.
- Do not break before a parenthesis (see bad example).
- A single method must not exceed 80 lines (including signature, braces, code, blank lines, line breaks, and any invisible characters, but excluding comments).
- Insert a single blank line between code blocks of different logic, semantics, or business purpose to improve readability.
- Note: never insert multiple blank lines where one suffices.
OOP Rules
- Variable‑argument (
...) parameters may be used only when the parameter types are the same and the business meaning is identical; avoid usingObjectas the var‑arg type.- Note: var‑args must be the last parameters in the list (prefer to avoid them altogether).
- Good example:
public List<User> listUsers(String type, Long... ids) { … }
- Calling
equalson an object that might benullcan cause NPE; invokeequalson a constant or a guaranteed‑non‑null object.- Good:
"test".equals(param); - Bad:
param.equals("test"); - Recommendation: use
java.util.Objects.equals(a, b)(available since JDK 7).
- Good:
- Compare values of boxed integer types using
equals, not==.- Explanation:
Integervalues between –128 and 127 are cached and may be==‑equal, but values outside that range are distinct objects; always useequalsto avoid subtle bugs.
- Explanation:
- For floating‑point numbers, never use
==on primitive types, and do not useequalson boxed types.- Explanation: floating‑point numbers are stored as “mantissa + exponent”; binary representation cannot exactly represent most decimal fractions.
- Monetary amounts must be stored as the smallest currency unit using an integer type.
- Compare
BigDecimalvalues withcompareTo()rather thanequals().- Explanation:
equals()checks both value and scale (1.0vs1.00are not equal), whilecompareTo()ignores scale.
- Explanation:
- Never create a
BigDecimalfrom adoublevia the constructor, as it can introduce precision loss.- Example of the problem:
new BigDecimal(0.1F)actually stores0.100000001490116119384765625. - Preferred: use the
Stringconstructor orBigDecimal.valueOf, which internally usesDouble.toStringto obtain a correctly rounded representation.BigDecimal recommend1 = new BigDecimal("0.1"); BigDecimal recommend2 = BigDecimal.valueOf(0.1);
- Example of the problem:
- Primitive vs. wrapper usage guidelines:
- All POJO fields must use wrapper types.
- RPC method parameters and return types must use wrapper types.
- Local variables should use primitive types.
- Rationale: POJO fields without initial values force callers to assign explicitly, preventing accidental NPEs; wrappers also allow
nullto convey extra information (e.g., remote‑call failure). - Bad example: using a primitive to receive a possibly‑null DB column leads to NPE.
- Bad example: a transaction report shows a percentage change as
0%when the RPC call fails and returns the default primitive value; using a wrapper would allownullto be displayed as a dash (-).
- When defining DO/PO/DTO/VO classes, do not assign default values to any fields.
- When adding new fields to a serializable class, do not modify the
serialVersionUIDunless the change is intentionally incompatible; otherwise deserialization will fail. - Constructors must contain no business logic; place initialization code in an
initmethod instead. - POJO classes must implement
toString; use the IDE’s generation tools.
Originally written by Li Wei (李唯_) and published in Chinese on 后端技术栈全书 (Full-Stack Backend Engineering). Translated and adapted for DriftSeas with permission.