Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Design, programming basics & common sense coding preferences

Table of contents

Design and programming basics (mostly for Java)

Basics

Design

Testing and code review

Concurrency

Other

Common sense coding preferences (mostly for Java)

Code generation

Lombok

Restrict its use to the bare minimum.

  • @Slf4j
  • @Getter
  • @Setter
  • @Data if you want both get/set
  • @EqualsAndHashcode if @Data is not used and you want to be explicit about the fields

Misc

  • Stick to plain Java and IDE code generation as much as possible without adding too much cruft
  • IDE assisted equals/hashcode if needed to customize objects stored in high performance collections

DI

Guice

Stick to explicit Java in all places other than the one place where you bind things.

  • No Guice annotations in any class other than AbstractModules
  • Rely on @Provides and @Singleton annotated methods to instantiate objects explicitly and bind/return them
  • Plain Java constructors invoked explicitly by the module's provider methods
  • Debatable: Use @Inject on 1 method in the module (which itself is injected) to contain the module's startup logic
public class FruitModule extends AbstractModule {
  @Provides
  Apple makeApple(){
   return new Apple();
  }

  @Provides
  @Singleton
  Flour makeFlour(){
   return new WheatFlour();
  }

  @Provides
  ApplePie makeApplePie(Apple apple, Flour flour){
   return new ApplePie(apple, flour);
  }
}

Lifecycle

  • Autocloseable if cleanup is required for manual try/finally - typically short lived but expensive objects with resources
  • @PreDestroy if the cleanup needs to be handled during shutdown - typically long lived objects

Style

Visibility

  • private + final in most cases
  • package-local + final if sub-class needs it
  • package-local method, class, constructor by default unless otherwise needed
  • @VisibleForTesting if package-local or protected method needs to be exposed for testing. Generally should be avoided as it may leak encapsulation
  • @VisibleForTesting if package-local or protected constructors on the other hand are much better as they declare all their dependencies upfront
  • protected constructor and perhaps fields of abstract classes that will be sub-classed from other packages

Member variables

  • Non-static Fields are initialized in the constructor
  • Initialization at the site of field declaration is nice only if:
  • It is a static field
  • Non-static field but the field needs a default value when used in conjunction with Lombok or Hibernate validator
@Getter
@Setter
public class PieRecipe {
    private static final int DEFAULT_FLOUR_GRAMS = 250;

    @NotBlank
    private String name;

    @Min(0)
    private int sugarGrams;

    @Min(100)
    private int flourGrams = DEFAULT_FLOUR_GRAMS;

    @Valid
    private List<Flavor> optionalFlavors;
}

Exceptions

No checked exceptions.

Formatting

Prefer readability over compactness while formatting code. Sometimes the IDE's automatic formatting may have to be overridden manually.

public BigClass(
        int foo, String bar, float fizz, Object bizz, List<String> names) {
  ...
}

public BigClass(
        int foo, String bar, float fizz, Object bizz, 
        List<String> names, Map<String, Long> moreNames) {
  ...
}

void hello()  {
    world(
      xx, yy, zz, aa, bb, more, stillMore, manyMore, 
      others, moreOthers.values().stream().count()
    );
}

List<Integer> ids = allObjects
          .keySet()
          .stream()
          .filter(foo -> foo > 34)
          .map()
          .collect(Collectors.toList());
          
List<String> items = few
      ? new ImmutableList.Builder()
            .add("foo")
            .add("bar")
            .build()
      : Lists.newArrayList("aa", "bb", "cc", "dd", "ee", "ff", ....);

Ordering of methods

//In unit tests.

@BeforeClass
@BeforeMethod
@AfterMethod
@AfterClass
@Test testXX
@Test testYY
...

Thread safety

  • By default all classes and methods are assumed not safe
  • Thread safe classes are marked by an explicit @ThreadSafe annotation

Naming

  • Avoid plurals in package names
  • Prefer plural form of class name if it is a Factory or something similar. Ex: JobExecutors or FooBuilders or Constants
  • Some pointers from Java 8 Time API: this & that

Constants

public final class FooBarConstants {
    //"PROP" or "KEY" prefix for property or map keys.
    public static final String PROP_CONFIG_FILE_NAME = "app.config.file";
    
    //"VAL" prefix for fixed property or map values.
    public static final String VAL_DEFAULT_CONFIG_FILE_NAME = "conf/default.properties";
    
    //"VAR" prefix for template strings that can vary slightly.
    public static final String VAR_ERR_MSG_AGE = "The value [%s] provided for 'age' is invalid";
    
    //Private ctor and an abstract class. Prevent sub-classing and instantiation.
    private FooBarConstants() {
    }
}

(Java)Docs, comments and logs

  • First time reference to nouns (Class or actors) use the literal names with correct capitalization or if available {@link LoanProcessor} or {@code Approval agent}. Subsequent references may be in lower case (the loan processor retries using ...)
  • Follow proper punctuation and capitalization rules
  • Log and exception messages have parameters wrapped within brackets. Ex: log.error("The directory [{}] does not exist", dirName.toString())

Non-Java files and resources

Prefer non-source code file and directory names like - ~/data-feed/all-lower-case-with-hyphens.csv.

Unit tests

We tend to focus on the "core" parts of the test but often ignore to clarify test boundaries, confirm the absence of unwanted start/end states and test for side effects.

  • Establish the test boundary and system under test (a specific method of a specific class when unit testing). This means using the Assert.assertXXX methods even before the test mutates the state. i.e confirm your assumptions. It is also a great way to document the behavior and makes code reviews easy Ex: assertEquals(0, customers.size())
  • Confirm that the parts that are not being tested and should not be affected are indeed untouched. i.e side-effect free. So, if mock objects are being used, it means doing something like:
  Mockito
     .verify(xx, never())
     .fooBar()
  • Using Assert.fail(xx) more often to confirm that certain test code paths are not executed. Ex:
   try { 
     //Actual test that triggers config ex
     ... 
     Assert.fail("Should've thrown config ex")
   } catch(ExpectedConfigException e) { 
     log.debug("Expected", e)
   }
@AshwinJay

This comment has been minimized.

Show comment Hide comment
@AshwinJay

AshwinJay Jan 28, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment