Fundamentals 27 min read

Why Boilerplate Code Still Matters: Tips for Faster, Cleaner Java Development

This article explores the concept of boilerplate code, its historical analogy to movable‑type printing, the benefits of reusing standard code snippets, practical ways to write, reduce, and optimise boilerplate in Java—including utility classes, enums, model definitions, collection constants, array constants, and complex condition handling.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
Why Boilerplate Code Still Matters: Tips for Faster, Cleaner Java Development

Boilerplate Code Overview

In the Northern Song dynasty, scientist Shen Kuo described movable‑type printing in Dream Pool Essays , noting that Bi Sheng's method allowed rapid production of many copies. This historical technique is used as an analogy for modern "boilerplate code".

During the Qingli era, a commoner named Bi Sheng invented movable type. Each character was cast in clay, thin as a coin, and hardened by fire. Printing a few copies was not easy, but printing dozens, hundreds, or thousands was extremely fast.

Just as movable type provides reusable characters, developers copy and modify standard code snippets—boilerplate—to speed up coding, improve consistency, and reduce errors.

What Is Boilerplate Code?

Boilerplate code (also called template code) refers to blocks of code with a fixed pattern that can be widely applied across different modules. A typical example is file‑reading code.

<span>try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {</span>
<span>    String line;</span>
<span>    while (Objects.nonNull(line = reader.readLine())) {</span>
<span>        // process a line</span>
<span>        ...</span>
<span>    }</span>
<span>} catch (IOException e) {</span>
<span>    String message = String.format("Read file (%s) exception", fileName);</span>
<span>    log.error(message, e);</span>
<span>    throw new ExampleException(message, e);</span>
<span>}</span>

Benefits of Boilerplate Code

Provides a standard example for newcomers to learn and quickly start coding.

Offers ready‑made solutions for similar problems, accelerating development.

Helps accumulate experience and continuously optimise common patterns.

Improves code quality because boilerplate has been battle‑tested.

Boosts coding speed by copy‑paste‑modify workflow.

Ensures uniform code style across the codebase.

How to Write Boilerplate Code

Common techniques include copy‑paste generation, text‑replace generation, Excel‑formula generation, IDE plugins (e.g., generating getters/setters, constructors, toString, DAO methods), and code‑generation tools that produce code based on predefined templates.

Reducing Boilerplate Code

Boilerplate can be reduced by using annotations, frameworks, and design patterns.

Using Annotations

For JavaBean getters/setters, Lombok's @Getter and @Setter annotations eliminate repetitive methods.

<span>@Getter</span>
<span>@Setter</span>
<span>public class User {</span>
<span>    private Long id;</span>
<span>    // other fields</span>
<span>}</span>

Using Frameworks

MyBatis abstracts JDBC boilerplate, allowing simple XML or annotation mappings.

<span>@Mapper</span>
<span>public interface UserDAO {</span>
<span>    List<EmployeeDO> queryEmployee(@Param("companyId") Long companyId);</span>
<span>}</span>
<span><mapper namespace="com.example.repository.UserDAO"></span>
<span>    <select id="queryEmployee" resultType="com.example.repository.UserDO"></span>
<span>        SELECT id, name, ... FROM t_user WHERE company_id = #{companyId}</span>
<span>    </select></span>
<span></mapper></span>

Using Design Patterns

Encapsulate repetitive logic with patterns such as the Template Method.

<span>public static void readLine(String fileName, Consumer<String> lineConsumer) {</span>
<span>    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {</span>
<span>        String line;</span>
<span>        while (Objects.nonNull(line = reader.readLine())) {</span>
<span>            lineConsumer.accept(line);</span>
<span>        }</span>
<span>    } catch (IOException e) {</span>
<span>        String message = String.format("Read file (%s) exception", fileName);</span>
<span>        log.error(message, e);</span>
<span>        throw new ExampleException(message, e);</span>
<span>    }</span>
<span>}</span>
<span>// usage</span>
<span>readLine("example.txt", line -> { /* process line */ });</span>

Unavoidable Boilerplate

Some boilerplate cannot be eliminated because it resides in underlying frameworks or language specifications. Recognising when to reuse rather than eliminate it is essential.

Defining Utility Classes

A typical utility class provides constants and helper methods.

<span>/** Example utility class */</span>
<span>public final class ExampleHelper {</span>
<span>    public static final int CONST_VALUE = 123;</span>
<span>    public static int sum(int a, int b) { return a + b; }</span>
<span>}</span>

Common issues include non‑standard modifier order and accidental inheritance. The best practice is to make the class final, provide a private constructor that throws UnsupportedOperationException, and keep all members static.

<span>/** Example utility class */</span>
<span>public final class ExampleHelper {</span>
<span>    public static final int CONST_VALUE = 123;</span>
<span>    private ExampleHelper() { throw new UnsupportedOperationException(); }</span>
<span>    public static int sum(int a, int b) { return a + b; }</span>
<span>}</span>

Defining Enum Classes

Enums should use primitive types for fields and mark them final to prevent modification.

<span>public enum ExampleEnum {</span>
<span>    ONE(1, "one(1)"),</span>
<span>    TWO(2, "two(2)"),</span>
<span>    THREE(3, "three(3)");</span>
<span>    private final int value;</span>
<span>    private final String desc;</span>
<span>    ExampleEnum(int value, String desc) { this.value = value; this.desc = desc; }</span>
<span>    public int getValue() { return value; }</span>
<span>    public String getDesc() { return desc; }</span>
<span>}</span>

Defining Model Classes

JavaBean Style

<span>public class User {</span>
<span>    private Long id;</span>
<span>    private String name;</span>
<span>    private Integer age;</span>
<span>    private String desc;</span>
<span>    public Long getId() { return id; }</span>
<span>    public void setId(Long id) { this.id = id; }</span>
<span>    // other getters/setters</span>
<span>}</span>

Overloaded Constructors

<span>public final class User {</span>
<span>    private Long id;</span>
<span>    private String name;</span>
<span>    private Integer age;</span>
<span>    private String desc;</span>
<span>    public User(Long id, String name) { this(id, name, null, null); }</span>
<span>    public User(Long id, String name, Integer age) { this(id, name, age, null); }</span>
<span>    public User(Long id, String name, Integer age, String desc) {</span>
<span>        Assert.notNull(id, "ID cannot be null");</span>
<span>        Assert.notNull(name, "Name cannot be null");</span>
<span>        this.id = id; this.name = name; this.age = age; this.desc = desc;</span>
<span>    }</span>
<span>    // getters only – immutable</span>
<span>}</span>

Builder Pattern

<span>public final class User {</span>
<span>    private Long id;</span>
<span>    private String name;</span>
<span>    private Integer age;</span>
<span>    private String desc;</span>
<span>    private User(Builder builder) {</span>
<span>        this.id = builder.id; this.name = builder.name; this.age = builder.age; this.desc = builder.desc;</span>
<span>    }</span>
<span>    public static Builder newBuilder(Long id, String name) { return new Builder(id, name); }</span>
<span>    public static class Builder {</span>
<span>        private final Long id;</span>
<span>        private final String name;</span>
<span>        private Integer age;</span>
<span>        private String desc;</span>
<span>        private Builder(Long id, String name) { Assert.notNull(id, "ID cannot be null"); Assert.notNull(name, "Name cannot be null"); this.id = id; this.name = name; }</span>
<span>        public Builder age(Integer age) { this.age = age; return this; }</span>
<span>        public Builder desc(String desc) { this.desc = desc; return this; }</span>
<span>        public User build() { return new User(this); }</span>
<span>    }</span>
<span>}</span>
<span>// usage</span>
<span>User user = User.newBuilder(1L, "alibaba").age(102).desc("test").build();</span>

Defining Collection Constants

Immutable collections should be created via Collections.unmodifiableXXX to prevent accidental modification.

<span>public final class ExampleHelper {</span>
<span>    public static final List<Integer> CONST_VALUE_LIST = Collections.unmodifiableList(Arrays.asList(1, 2, 3));</span>
<span>    public static final Set<Integer> CONST_VALUE_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(1, 2, 3)));
<span>    public static final Map<Integer, String> CONST_VALUE_MAP;</span>
<span>    static {</span>
<span>        Map<Integer, String> map = new HashMap<>();</span>
<span>        map.put(1, "value1"); map.put(2, "value2"); map.put(3, "value3");</span>
<span>        CONST_VALUE_MAP = Collections.unmodifiableMap(map);</span>
<span>    }</span>
<span>}</span>

Defining Array Constants

Expose a cloned copy of a private array to keep the original immutable.

<span>public final class ExampleHelper {</span>
<span>    private static final int[] CONST_VALUES = new int[] {1, 2, 3};</span>
<span>    public static int[] getConstValues() { return CONST_VALUES.clone(); }</span>
<span>}</span>

Handling Complex Conditional Logic

When many conditions are combined with && or ||, the cyclomatic complexity grows. One approach is to store each condition as a Predicate in an immutable list and evaluate them sequentially.

<span>private static final List<Predicate<AuditDataVO>> AUDIT_RESULT_PREDICATE_LIST =
<span>    Collections.unmodifiableList(Arrays.asList(</span>
<span>        data -> isPassed(data.getAuditItem1()),</span>
<span>        data -> isPassed(data.getAuditItem2()),</span>
<span>        // ... up to item11</span>
<span>        data -> isPassed(data.getAuditItem11())
<span>    ));</span>
<span>private static AuditResult getAuditResult(AuditDataVO data) {</span>
<span>    for (Predicate<AuditDataVO> p : AUDIT_RESULT_PREDICATE_LIST) {</span>
<span>        if (!p.test(data)) return AuditResult.REJECTED;</span>
<span>    }</span>
<span>    return AuditResult.PASSED;</span>
<span>}</span>

Conclusion

Boilerplate code, while sometimes unavoidable, can be managed effectively through annotations, frameworks, design patterns, immutable collections, and thoughtful API design, leading to faster development, higher code quality, and more maintainable projects.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsJavaCode Optimizationbest practicesBoilerplate Code
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.