Home

Development Guidelines

Li

Li Wei

April 25, 202512 min read

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/else branches, 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 = +1
  • for / while / do-while = +1
  • each branch in a case condition (switch) = +1
  • && / || logical operators = +1

Example of actual complexity calculation:

  • Nodes (N): 3 conditional statements (if, else if, else) plus two return statements.
  • Edges (E): the code paths split into 3 branches – executing if, executing else if, executing else.
  • 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, EclEmma plugins 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 SonarQube to locate highly coupled classes/modules.
    • Java’s JDepend tool can quantify class dependency relationships.

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, CheckStyle detect 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
  • Method names, parameter names, member variables, and local variables all use lowerCamelCase.
    • Good example: ForceCode, UserDO, HtmlDTO, XmlService, TcpUdpDeal, TaPromotion
  • Constants are all uppercase with underscores separating words, aiming for clear, complete semantics.
    • Good example: MAX_STOCK_COUNT, CACHE_EXPIRED_TIME
  • Abstract classes start with Abstract or Base; exception classes end with Exception; test classes start with the name of the class they test and end with Test.
  • 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 from is_xxx to xxx must be established.
    • Bad example: a Boolean field isDeleted whose getter is also isDeleted(). Some frameworks think the underlying field is deleted, causing missing data or exceptions.
  • 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; class MessageUtils
  • 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.
  • Do not use obscure English abbreviations that are hard to understand.
    • Bad examples: AbsClass for AbstractClass, condi for condition, Fu for Function. Such shortcuts severely reduce readability.
  • When naming constants and variables, place the type‑indicating noun at the end to improve recognizability.
    • Good example: startTime, workQueue, nameList, TERMINATED_THREAD_COUNT
  • 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;
  • 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 constant String COMPANY = "alibaba";
    • Bad example: public abstract void commit();
  • Naming rules for interfaces and their implementations:
    • Mandatory: For Service and DAO layers, expose a service as an interface; the implementation class uses the Impl suffix.
      • Good example: CacheServiceImpl implements CacheService.
    • Recommended: If the interface describes a capability, use an adjective ending in ‑able as the interface name.
      • Good example: AbstractTranslator implements Translatable.
  • 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: ProcessStatusEnum with members SUCCESS, 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 list and 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.
    • Domain model naming:
      • Data Object: xxxDO (where xxx is 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.

Constant Definition

  • No “magic numbers” (undeclared literals) may appear directly in code.
  • Floating‑point literals must use uppercase D or F suffixes.
    • Good example: public static final double HEIGHT = 175.5D; public static final float WEIGHT = 150.3F;
  • 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 in SystemConfigConsts.
  • 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.jarconstant directory.
    • Application‑internal shared: placed in a first‑party library, usually under a module’s constant directory.
    • 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 be true but returns false, causing production issues.
    • Sub‑project shared: constants under the current sub‑project’s constant directory.
    • Package shared: constants under a constant directory within the package.
    • Class shared: private static final constants defined directly inside the class.
  • 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 using Object as 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 equals on an object that might be null can cause NPE; invoke equals on 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).
  • Compare values of boxed integer types using equals, not ==.
    • Explanation: Integer values between –128 and 127 are cached and may be ==‑equal, but values outside that range are distinct objects; always use equals to avoid subtle bugs.
  • For floating‑point numbers, never use == on primitive types, and do not use equals on 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 BigDecimal values with compareTo() rather than equals().
    • Explanation: equals() checks both value and scale (1.0 vs 1.00 are not equal), while compareTo() ignores scale.
  • Never create a BigDecimal from a double via the constructor, as it can introduce precision loss.
    • Example of the problem: new BigDecimal(0.1F) actually stores 0.100000001490116119384765625.
    • Preferred: use the String constructor or BigDecimal.valueOf, which internally uses Double.toString to obtain a correctly rounded representation.
      BigDecimal recommend1 = new BigDecimal("0.1");
      BigDecimal recommend2 = BigDecimal.valueOf(0.1);
      
  • 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 null to 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 allow null to 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 serialVersionUID unless the change is intentionally incompatible; otherwise deserialization will fail.
  • Constructors must contain no business logic; place initialization code in an init method 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.

Keep reading

More related articles from DriftSeas.