Bad programming practices

Refefence: https://www.toptal.com/spring/top-10-most-common-spring-framework-mistakes

Going Too Low Level

https://en.wikipedia.org/wiki/Not_invented_here

The “not invented here” syndrome is quite common in the software development world. Symptoms include regularly rewriting pieces of commonly used code and a lot of developers seems to suffer from it.

While understanding the internals of a particular library and its implementation is for the most part good and necessary (and can be a great learning process as well), it’s detrimental to your development as a software engineer to be constantly tackling the same low-level implementation details. There is a reason why abstractions and frameworks such as Spring exist, which is precisely to separate you from repetitive manual work and allow you to concentrate on higher level details - your domain objects and business logic.

So embrace the abstractions - the next time you are faced with a particular problem, do a quick search first and determine whether a library solving that problem is already integrated into Spring; nowadays, chances are you’ll find a suitable existing solution.

Examples:

Project Lombok

‘Leaking’ Internals

Exposing your internal structure is never a good idea because it creates inflexibility in service design and consequently promotes bad coding practices.

Examples:

  1. Making database entities accessible from API endpoints. Making changes to your database back-end should not require any additional changes in the service layer. Use DTOs.

Lacking Separation of Concerns

As your application grows, code organization increasingly starts becoming an ever more important matter. Ironically, most of the good software engineering principles start to break down at scale – especially in cases where not much thought has been given to the application architecture design. One of the most common mistakes developers then tend to succumb to is mixing code concerns, and it’s extremely easy to do!

What usually breaks separation of concerns is just ‘dumping’ new functionality into existing classes. This is, of course, a great short-term solution (for starters, it requires less typing) but it inevitably becomes a problem further down the road, be it during testing, maintenance, or somewhere in between.

Examples:

  1. Invoking repository layer methods from Controllers.

Inconsistency

The topic of consistency is not necessarily exclusive to Spring (or Java, for that matter), but still is an important facet to consider when working on Spring projects. While coding style can be up for debate (and is usually a matter of agreement within a team or within an entire company), having a common standard turns out to be a great productivity aid. This is especially true with multi-person teams; consistency allows hand-off to occur without many resources being spent on hand-holding or providing lengthy explanations regarding the responsibilities of different classes

Consider a Spring project with its various configuration files, services and controllers. Being semantically consistent in naming them creates an easily searchable structure where any new developer can manage his way around the code; appending Config suffixes to your configuration classes, Service suffixes to your services and Controller suffixes to your controllers, for example.

Poor Error Handling

Error handling on the server-side deserves a specific emphasis. If you ever had to handle exception responses from a poorly written API, you probably know why– it can be a pain to properly parse exceptions, and even more painful to determine the reason for why those exceptions occurred in the first place.

As an API developer, you’d ideally want to cover all user-facing endpoints and translate them into a common error format. This usually means having a generic error code and description rather than the cop-out solution of a) returning a “500 Internal Server Error” message, or b) just dumping the stack trace to the user (which should actually be avoided at all costs since it exposes your internals in addition to being difficult to handle client-side).

Something similar to this is commonly encountered in most popular APIs, and tends to work well since it can be easily and systematically documented. Translating exceptions into this format can be done by providing the @ExceptionHandler annotation to a method.

@Value
public class ErrorResponse {

    private Integer errorCode;
    private String errorMessage;

}

Improperly Dealing with Multithreading

Regardless of whether it is encountered in desktop or web apps, Spring or no Spring, multithreading can be a tough nut to crack. Problems caused by parallel execution of programs are nerve-wrackingly elusive and often times extremely difficult to debug - in fact, due to the nature of the problem, once you realise you’re dealing with a parallel-execution issue you’re probably going to have to forego the debugger entirely and inspect your code “by hand” until you find the root error cause. Unfortunately, a cookie-cutter solution does not exists for solving such issues; depending on your specific case, you’re going to have to assess the situation and then attack the problem from the angle you deem is best.

Ideally you would, of course, want to avoid multithreading bugs altogether. Again, a one-size-fits-all approach does not exist for doing so, but here are some practical considerations for debugging and preventing multithreading errors:

Avoid Global State

First, always remember the “global state” issue. If you’re creating a multithreaded application, absolutely anything that is globally modifiable should be closely monitored and, if possible, removed altogether. If there is a reason why the global variable must remain modifiable, carefully employ synchronization and track your application’s performance to confirm that it’s not sluggish due to the newly introduced waiting periods.

https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

Avoid Mutability

https://en.wikipedia.org/wiki/Functional_programming

This one comes straight from functional programming and, adapted to OOP, states that class mutability and changing state should be avoided. This, in short, means foregoing setter methods and having private final fields on all your model classes. The only time their values are mutated is during construction. This way you can be certain that no contention problems arise and that accessing object properties will provide the correct values at all times.

Log Crucial Data

Assess where your application might cause trouble and preemptively log all crucial data. If an error occurs, you will be grateful to have information stating which requests were received and have better insight into why your application misbehaved. It’s again necessary to note that logging introduces additional file I/O and should therefore not be abused as it can severely impact your application’s performance.

Reuse Existing Implementations

http://www.nurkiewicz.com/2014/11/executorservice-10-tips-and-tricks.html

http://www.nurkiewicz.com/2013/05/java-8-definitive-guide-to.html

http://docs.spring.io/spring-framework/docs/4.0.x/spring-framework-reference/html/mvc.html#mvc-ann-async

Whenever you are in need of spawning your own threads (e.g. for making async requests to different services), reuse existing safe implementations rather than create your own solutions. This will, for the most part, mean utilizing ExecutorServices and Java 8’s neat functional-style CompletableFutures for thread creation. Spring also allows asynchronous request processing via the DeferredResult class.

Not Employing Annotation-Based Validation

(Still) Using An XML-Based Configuration

Not using Profiles

Spring profiles

Failing to Embrace Dependency Injection

  1. Inversion of Control
  2. Dependency Injection pattern

Lack of Testing, or Improper Testing

Testing

Cyclic Dependency

Not following the basic doctrines of Singleton Class declaration like using instance variable in singleton scope beans.

Cognitive and Cyclomatic complexity

Not following Code aesthetics