11 Mistakes JAVA Developers make when using EXCEPTIONS

The problem: If you use Exceptions in the wrong way, bugs will be very difficult to find. If you always use generic Exceptions, how can other developers know what error has occurred? You have to understand why we use Exceptions and how to use them effectively!

See the 11 mistakes Java Developers make when using Exceptions.

First, let’s see the hierarchy of the Exception classes.

Checked Exceptions / Unchecked Exceptions

1 – Using only the Exception class

It’s a common mistake that developers specifically catch the Exception class for any error. It’s much more difficult to identify the error if you see only an Exception being caught. The solution to this problem is to create specific Exceptions – but watch out, not too specific!

2 – Creating lots of specific Exceptions

Don’t create Exceptions for everything. Your application will be full of classes, useless repetition and you will create unnecessary work. Instead, create Exceptions for really important business requirements. For example, if you are developing a bank system one possible Exception would be when trying to withdraw money and the balance is zero: BalanceNotAvailableException. Another Exception would be transferring money to another person and the account does not exist, you can create: BankAccountNotFoundException and show an understandable Exception message to the user.

RuntimeException could be used when the bank’s server is out of service. Here you can use, for example: ServerNotAvailableException. The system must crash for this kind of error. There is no recovery.

3 – Creating a log for every catch

Logging every Exception catch will pollute your code. To prevent this, just log once and throw your Exception in the last catch. You won’t lose your Stacktrace if you wrap the Exception. If you are working with web applications, you should create a catch on your controller layer and log the error.

4 – Not knowing the difference between Checked and Unchecked Exceptions

When should Checked Exceptions be used? Use Checked when there is a recoverable error or an important business requirement.

The most common Checked Exception is the Exception class. Two related classes from Exception are FileNotFoundException and SQLException.You are obligated to handle or declare these exceptions. You must throw or catch the Exception or else it won’t compile.

When should Unchecked Exceptions be used? Use Unchecked when there is no recovery. For example, when the memory of the server is overused.

RuntimeException is used for errors when your application can not recover. For example, NullPointerException and ArrayOutOfBoundsException. You can avoid a RuntimeException with an ‘if’ command. You should not handle or catch it.

There is also the class Error. It is an Unchecked Exception too. Never try to catch or handle this kind of Exception. They are errors from the JVM and are the most serious kind of Exception in Java. You must analyze the cause of Exceptions like this and change your code.

5 –  Silencing Exceptions

Never catch the Exception and do nothing, for example:

1
2
3
4
5
try {
    System.out.println("Never do that!");
} catch (AnyException exception) {
    // Do nothing
}

The catch will be useless. It’s impossible to know what happened and the Exception will be silenced. The developer will be obliged to debug the code and see what happened. If we create a good log, the time-consuming analysis won’t be necessary.

6 – Not following the principle “throw early, catch late”

If you have to handle Exception, for example, in your Service, you should do two things:

  1. Wrap your Exception
  2. Throw the Exception to the last catch and handle it.

7 – Not using clear messages on the Exceptions

Always use clear messages on your Exceptions. Doing this will help a lot when finding errors. Even better, create a Properties File with all Exception messages. You can use the file on your View layer and show users messages about the business requirements.

8- Not cleaning up after handling the Exception

After using resources like files and database connection, clean them and close them so that you won’t harm the system’s performance. You can use the finally block to do it.

9 – Not documenting Exceptions with javadoc

To avoid headaches, always Document why the Exception is being thrown in your method. Document your Exception and explain why you created it.

10 – Never lose the Stacktrace

When wrapping an Exception in another one, don’t just throw the other Exception, keep the Stacktrace.

Bad code:

1
2
3
4
5
try {
    // Do the logic
} catch (BankAccountNotFoundException exception) {
    throw new BusinessException();
}

Good code:

1
2
3
4
5
try {
    // Do the logic
} catch (BankAccountNotFoundException exception) {
    throw new BusinessException(exception);
}

11 – Not organizing the hierarchy of specific Exceptions

If you don’t organize the hierarchy of your Exceptions, the relationship will be difficult between the parts of the system. You will have lots of problems.

You should use an hierarchy similar to this one:

Exception

BusinessException

AccountingException

HumanResourcesException
BillingCodeNotFoundException

  EmployeeNotFoundException

Different between session.get() and session.load()

Often times, you will notice Hibernate developers mix use of session.get() and session load(), do you wonder what’s the different and when you should use either of it?

Different between session.get() and session.load()

Drawing3

Actually, both functions are use to retrieve an object with different mechanism, of course.

1. session.load()

  • It will always return a “proxy” (Hibernate term) without hitting the database. In Hibernate, proxy is an object with the given identifier value, its properties are not initialized yet, it just look like a temporary fake object.
  • If no row found , it will throws an ObjectNotFoundException.

2. session.get()

  • It always hit the database and return the real object, an object that represent the database row, not proxy.
  • If no row found , it return null.

It’s about performance

Hibernate create anything for some reasons, when you do the association, it’s normal to obtain retrieve an object (persistent instance) from database and assign it as a reference to another object, just to maintain the relationship. Let’s go through some examples to understand in what situation you should use session.load().

1. session.get()

For example, in a Stock application , Stock and StockTransactions should have a “one-to-many” relationship, when you want to save a stock transaction, it’s common to declared something like below

Stock stock = (Stock)session.get(Stock.class, new Integer(2));
           StockTransaction stockTransactions = new StockTransaction();
           //set stockTransactions detail
           stockTransactions.setStock(stock);
           session.save(stockTransactions);

Output

Hibernate:
    select ... from mkyong.stock stock0_
    where stock0_.STOCK_ID=?
Hibernate:
    insert into mkyong.stock_transaction (...)
    values (?, ?, ?, ?, ?, ?)

In session.get(), Hibernate will hit the database to retrieve the Stock object and put it as a reference to StockTransaction. However, this save process is extremely high demand, there may be thousand or million transactions per hour, do you think is this necessary to hit the database to retrieve the Stock object everything save a stock transaction record? After all you just need the Stock’s Id as a reference to StockTransaction.

2. session.load()

In above scenario, session.load() will be your good solution, let’s see the example,

Stock stock = (Stock)session.load(Stock.class, new Integer(2));
           StockTransaction stockTransactions = new StockTransaction();
           //set stockTransactions detail
           stockTransactions.setStock(stock);
           session.save(stockTransactions);

Output

Hibernate:
    insert into mkyong.stock_transaction (...)
    values (?, ?, ?, ?, ?, ?)

In session.load(), Hibernate will not hit the database (no select statement in output) to retrieve the Stock object, it will return a Stock proxy object – a fake object with given identify value. In this scenario, a proxy object is enough for to save a stock transaction record.

Exception

In exception case, see the examples

session.load()

Stock stock = (Stock)session.load(Stock.class, new Integer(100)); //proxy

 //initialize proxy, no row for id 100, throw ObjectNotFoundException
System.out.println(stock.getStockCode());

It will always return a proxy object with the given identity value, even the identity value is not exists in database. However, when you try to initialize a proxy by retrieve it’s properties from database, it will hit the database with select statement. If no row is found, a ObjectNotFoundException will throw.

org.hibernate.ObjectNotFoundException: No row with the given identifier exists:
[com.mkyong.common.Stock#100]

session.get()

//return null if not found
Stock stock = (Stock)session.get(Stock.class, new Integer(100));
System.out.println(stock.getStockCode()); //java.lang.NullPointerException

It will always return null , if the identity value is not found in database.

Conclusion

There are no always correct solution, you have to understand the differential in between, and decide which method is best fix in your application.

Java NIO vs. IO

When should I use IO and when should I use NIO?

In this text I will try to shed some light on the differences between Java NIO and IO, their use cases, and how they affect the design of your code

Main Differences Betwen Java NIO and IO

The table below summarizes the main differences between Java NIO and IO. I will get into more detail about each difference in the sections following the table.

IO NIO
Stream oriented Buffer oriented
Blocking IO Non blocking IO
Selectors

Stream Oriented vs. Buffer Oriented

The first big difference between Java NIO and IO is that IO is stream oriented, where NIO is buffer oriented. So, what does that mean?

Java IO being stream oriented means that you read one or more bytes at a time, from a stream. What you do with the read bytes is up to you. They are not cached anywhere. Furthermore, you cannot move forth and back in the data in a stream. If you need to move forth and back in the data read from a stream, you will need to cache it in a buffer first.

Java NIO’s buffer oriented approach is slightly different. Data is read into a buffer from which it is later processed. You can move forth and back in the buffer as you need to. This gives you a bit more flexibility during processing. However, you also need to check if the buffer contains all the data you need in order to fully process it. And, you need to make sure that when reading more data into the buffer, you do not overwrite data in the buffer you have not yet processed.

Blocking vs. Non-blocking IO

Java IO’s various streams are blocking. That means, that when a thread invokes a read() or write(), that thread is blocked until there is some data to read, or the data is fully written. The thread can do nothing else in the meantime.

Java NIO’s non-blocking mode enables a thread to request reading data from a channel, and only get what is currently available, or nothing at all, if no data is currently available. Rather than remain blocked until data becomes available for reading, the thread can go on with something else.

The same is true for non-blocking writing. A thread can request that some data be written to a channel, but not wait for it to be fully written. The thread can then go on and do something else in the mean time.

What threads spend their idle time on when not blocked in IO calls, is usually performing IO on other channels in the meantime. That is, a single thread can now manage multiple channels of input and output.

Selectors

Java NIO’s selectors allow a single thread to monitor multiple channels of input. You can register multiple channels with a selector, then use a single thread to “select” the channels that have input available for processing, or select the channels that are ready for writing. This selector mechanism makes it easy for a single thread to manage multiple channels.

How NIO and IO Influences Application Design

Whether you choose NIO or IO as your IO toolkit may impact the following aspects of your application design:

  1. The API calls to the NIO or IO classes.
  2. The processing of data.
  3. The number of thread used to process the data.

 

The API Calls

Of course the API calls when using NIO look different than when using IO. This is no surprise. Rather than just read the data byte for byte from e.g. an InputStream, the data must first be read into a buffer, and then be processed from there.

The Processing of Data

The processing of the data is also affected when using a pure NIO design, vs. an IO design.

In an IO design you read the data byte for byte from an InputStream or a Reader. Imagine you were processing a stream of line based textual data. For instance:

Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

This stream of text lines could be processed like this:

InputStream input = ... ; // get the InputStream from the client socket

BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

Notice how the processing state is determined by how far the program has executed. In other words, once the first reader.readLine() method returns, you know for sure that a full line of text has been read. The readLine() blocks until a full line is read, that’s why. You also know that this line contains the name. Similarly, when the second readLine() call returns, you know that this line contains the age etc.

As you can see, the program progresses only when there is new data to read, and for each step you know what that data is. Once the executing thread have progressed past reading a certain piece of data in the code, the thread is not going backwards in the data (mostly not). This principle is also illustrated in this diagram:

nio-vs-io-1
Java IO: Reading data from a blocking stream.

A NIO implementation would look different. Here is a simplified example:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

Notice the second line which reads bytes from the channel into the ByteBuffer. When that method call returns you don’t know if all the data you need is inside the buffer. All you know is that the buffer contains some bytes. This makes processing somewhat harder.

Imagine if, after the first read(buffer) call, that all what was read into the buffer was half a line. For instance, “Name: An”. Can you process that data? Not really. You need to wait until at leas a full line of data has been into the buffer, before it makes sense to process any of the data at all.

So how do you know if the buffer contains enough data for it to make sense to be processed? Well, you don’t. The only way to find out, is to look at the data in the buffer. The result is, that you may have to inspect the data in the buffer several times before you know if all the data is inthere. This is both inefficient, and can become messy in terms of program design. For instance:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

while(! bufferFull(bytesRead) ) {
    bytesRead = inChannel.read(buffer);
}

The bufferFull() method has to keep track of how much data is read into the buffer, and return either true or false, depending on whether the buffer is full. In other words, if the buffer is ready for processing, it is considered full.

The bufferFull() method scans through the buffer, but must leave the buffer in the same state as before the bufferFull() method was called. If not, the next data read into the buffer might not be read in at the correct location. This is not impossible, but it is yet another issue to watch out for.

If the buffer is full, it can be processed. If it is not full, you might be able to partially process whatever data is there, if that makes sense in your particular case. In many cases it doesn’t.

The is-data-in-buffer-ready loop is illustrated in this diagram:

nio-vs-io-2
Java NIO: Reading data from a channel until all needed data is in buffer.

 

Summary

NIO allows you to manage multiple channels (network connections or files) using only a single (or few) threads, but the cost is that parsing the data might be somewhat more complicated than when reading data from a blocking stream.

If you need to manage thousands of open connections simultanously, which each only send a little data, for instance a chat server, implementing the server in NIO is probably an advantage. Similarly, if you need to keep a lot of open connections to other computers, e.g. in a P2P network, using a single thread to manage all of your outbound connections might be an advantage. This one thread, multiple connections design is illustrated in this diagram:

nio-vs-io-3
Java NIO: A single thread managing multiple connections.

If you have fewer connections with very high bandwidth, sending a lot of data at a time, perhaps a classic IO server implementation might be the best fit. This diagram illustrates a classic IO server design:

nio-vs-io-4
Java IO: A classic IO server design – one connection handled by one thread.

Difference between TRUNCATE, DELETE and DROP?

DELETE and TRUNCATE are two SQL commands used to remove records from a particular table. But they differ in how they execute and operate.
–> DELETE:

1. Removes Some or All rows from a table.

2. A WHERE clause can be used to remove some rows. If no WHERE condition is specified, all rows will be removed.

3. Causes all DELETE triggers on the table to fire.

4. It removes rows row-by-row one at a time and records an entry in the Transaction logs, thus is slower than TRUNCATE.

5. Every deleted row in locked, thus it requires more number of locks and database resources.

6. According to MS BOL, if a table is a Heap or no Clustered index is defined than the row-pages emptied are not de-allocated instantly and remain allocated in the heap. Thus, no other object can reuse this associated space. Thus to de-allocate the space a Clustered index is required or TABLOCK hint should be applied in the DELETE statement.

7. This is a DML command as it is just used to manipulate/modify the table data. It does not change any property of a table.


 

–> TRUNCATE:

1. Removes All rows from a table.

2. Does not require a WHERE clause, so you can not filter rows while Truncating.

3. With SQL Server 2016 you can Truncate a Table Partition, for more details check [here].

4. IDENTITY columns are re-seeded on this operation, if no seed was defined then the default value 1 is used.

5. No Triggers are fired on this operation because it does not operate on individual rows.

6. It de-allocates Data Pages instead of Rows and records Data Pages instead of Rows in Transaction logs, thus is faster than DELETE.

7. While de-allocating Pages it locks Pages and not Rows, thus it requires less number of locks and few resources.

8. TRUNCATE is not possible when a table:
a. is reference by a Foreign Key or tables used in replication or with Indexed views.
b. participates in an Indexed/Materialized View.
c. published by using Transactional/Merge replication.

9. This is a DDL command as it resets IDENTITY columns, de-allocates Data Pages and empty them for use of other objects in the database.

Note: It is a misconception among some people that TRUNCATE cannot be roll-backed. But in reality both DELETE and TRUNCATE operations can be COMMITTED AND ROLL-BACKED if provided inside a Transaction. The only method to Rollback a committed transaction after DELETE/TRUNCATE is to restore the last backup and run transactions logs till the time when DELETE/TRUNCATE is about to happen.


 

–> DROP:

1. The DROP TABLE command removes one or more table(s) from the database.

2. All related Data, Indexes, Triggers, Constraints, and Permission specifications for the Table are dropped by this operation.

3. Some objects like Views, Stored Procedures that references the dropped table are not dropped and must be explicitly dropped.

4. Cannot drop a table that is referenced by any Foreign Key constraint.

5. According to MS BOL, Large tables and indexes that use more than 128 extents are dropped in two separate phases: Logical and Physical. In the Logical phase, the existing allocation units used by the table are marked for de-allocation and locked until the transaction commits. In the physical phase, the IAM pages marked for de-allocation are physically dropped in batches.

 

****************************************************************************

****************************************************************************

DELETE
1. DELETE is a DML Command.
2. DELETE statement is executed using a row lock, each row in the table is locked for deletion.
3. We can specify filters in where clause
4. It deletes specified data if where condition exists.
5. Delete activates a trigger because the operation are logged individually.
6. Slower than truncate because, it keeps logs.
7. Rollback is possible.

TRUNCATE
1. TRUNCATE is a DDL command.
2. TRUNCATE TABLE always locks the table and page but not each row.
3. Cannot use Where Condition.
4. It Removes all the data.
5. TRUNCATE TABLE cannot activate a trigger because the operation does not log individual row deletions.
6. Faster in performance wise, because it doesn’t keep any logs.
7. Rollback is not possible.

DELETE and TRUNCATE both can be rolled back when used with TRANSACTION.

If Transaction is done, means COMMITED, then we can not rollback TRUNCATE command, but we can still rollback DELETE command from LOG files, as DELETE write records them in Log file in case it is needed to rollback in future from LOG files.

How To Stop A Thread In Java?

How do you stop a thread in java? now-a-days, this has been the popular question in the java interviews. Because, stop() method has been deprecated for some safety reasons. As stop() method has been deprecated, interviewer will be interested in what logic you will be using to stop a thread. There are two ways through which you can stop a thread in java. One is using boolean variable and second one is using interrupt() method. In this post, we will discuss both of these methods.

How To Stop A Thread In Java Using A boolean Variable?

In this method, we declare one boolean variable called flag in a thread. Initially we set this flag as true. Keep the task to be performed in while loop inside the run() method by passing this flag. This will make thread continue to run until flag becomes false. We have defined stopRunning() method. This method will set the flag as false and stops the thread. Whenever you want to stop the thread, just call this method. Also notice that we have declared flag as volatile. This will make thread to read its value from the main memory, thus making sure that thread always gets its updated value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class MyThread extends Thread
{
    //Initially setting the flag as true
    
    private volatile boolean flag = true;
    
    //This method will set flag as false
    
    public void stopRunning()
    {
        flag = false;
    }
    
    @Override
    public void run()
    {
        //Keep the task in while loop
        
        //This will make thread continue to run until flag becomes false
        
        while (flag)
        {
            System.out.println("I am running....");
        }
        
        System.out.println("Stopped Running....");
    }
}
public class MainClass
{  
    public static void main(String[] args)
    {
        MyThread thread = new MyThread();
        
        thread.start();
        
        try
        {
            Thread.sleep(100);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        
        //call stopRunning() method whenever you want to stop a thread
        
        thread.stopRunning();
    }   
}

Output :

I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
Stopped Running….

How To Stop A Thread In Java Using interrupt() Method?

In this method, we use interrupt() method to stop a thread. Whenever you call interrupt() method on a thread, it sets the interrupted status of a thread. This status can be obtained by interrupted() method. This status is used in a whileloop to stop a thread.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MyThread extends Thread
{   
    @Override
    public void run()
    {
        while (!Thread.interrupted())
        {
            System.out.println("I am running....");
        }
        
        System.out.println("Stopped Running.....");
    }
}
public class MainClass
{  
    public static void main(String[] args)
    {
        MyThread thread = new MyThread();
        
        thread.start();
        
        try
        {
            Thread.sleep(100);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        
        //interrupting the thread
        
        thread.interrupt();
    }   
}

Output :

I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
I am running….
Stopped Running…..

9 Anti-Patterns Every Programmer Should Be Aware Of

A healthy dose of self-criticism is fundamental to professional and personal growth. When it comes to programming, this sense of self-criticism requires the ability to detect unproductive or counter-productive patterns in designs, code, processes, and behaviour. This is why a knowledge of anti-patterns is very useful for any programmer. This article is a discussion of anti-patterns that I have found to be recurring, ordered roughly based on how often I have come across them, and how long it took to undo the damage they caused.

Some of the anti-patterns discussed have elements in common with cognitive biases, or are directly caused by them. Links to relevant cognitive biases are provided as we go along in the article. Wikipedia also has a nice list of cognitive biases for your reference.

And before starting, let’s remember that dogmatic thinking stunts growth and innovation so consider the list as a set of guidelines and not written-in-stone rules. And if I missed anything that you consider to be important, feel free to comment below!

1   Premature Optimization

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.Donald Knuth

Although never is often better than *right* now.Tim Peters, The Zen of Python

What is it?

Optimizing before you have enough information to make educated conclusions about where and how to do the optimization.

Why it’s bad

It is very difficult to know exactly what will be the bottleneck in practice. Attempting to optimize prior to having empirical data is likely to end up increasing code complexity and room for bugs with negligible improvements.

How to avoid it

Prioritize writing clean and readable code that works first, using known and tested algorithms and tools. Use profiling tools when needed to find bottlenecks and optimize the priorities. Rely on measurements and not guesses and speculation.

Examples and signs

Caching before profiling to find the bottlenecks. Using complicated and unproven “heuristics” instead of a known mathematically correct algorithm. Choosing a new and untested experimental web framework that can theoretically reduce request latency under heavy loads while you are in early stages and your servers are idle most of the time.

The tricky part

The tricky part is knowing when the optimization is premature. It’s important to plan in advance for growth. Choosing designs and platforms that will allow for easy optimization and growth is key here. It’s also possible to use “premature optimization” as an excuse to justify writing bad code. Example: writing an O(n2) algorithm to solve a problem when a simpler, mathematically correct, O(n) algorithm exists, simply because the simpler algorithm is harder to understand.

tl;dr

Profile before optimizing. Avoid trading simplicity for efficiency until it is needed, backed by empirical evidence.

2   Bikeshedding

Every once in a while we’d interrupt that to discuss the typography and the color of the cover. And after each discussion, we were asked to vote. I thought it would be most efficient to vote for the same color we had decided on in the meeting before, but it turned out I was always in the minority! We finally chose red. (It came out blue.)Richard Feynman, What Do You Care What Other People Think?

What is it?

Tendency to spend excessive amounts of time debating and deciding on trivial and often subjective issues.

Why it’s bad

It’s a waste of time. Poul-Henning Kamp goes into depth in an excellent email here.

How to avoid it

Encourage team members to be aware of this tendency, and to prioritize reaching a decision (vote, flip a coin, etc. if you have to) when you notice it. Consider A/B testing later to revisit the decision, when it is meaningful to do so (e.g. deciding between two different UI designs), instead of further internal debating.

Richard Feynman teaching a lecture.

Richard Feynman was not a fan of bikeshedding.

Examples and signs

Spending hours or days debating over what background color to use in your app, or whether to put a button on the left or the right of the UI, or to use tabs instead of spaces for indentation in your code base.

The tricky part

Bikeshedding is easier to notice and prevent in my opinion than premature optimization. Just try to be aware of the amount of time spent on making a decision and contrast that with how trivial the issue is, and intervene if necessary.

tl;dr

Avoid spending too much time on trivial decisions.

3   Analysis Paralysis

Want of foresight, unwillingness to act when action would be simple and effective, lack of clear thinking, confusion of counsel […] these are the features which constitute the endless repetition of history.Winston Churchill, Parliamentary Debates

Now is better than never.Tim Peters, The Zen of Python

What is it?

Over-analyzing to the point that it prevents action and progress.

Why it’s bad

Over-analyzing can slow down or stop progress entirely. In the extreme cases, the results of the analysis can become obsolete by the time they are done, or worse, the project might never leave the analysis phase. It is also easy to assume that more information will help decisions when the decision is a difficult one to make ― see information bias and validity bias.

How to avoid it

Again, awareness helps. Emphasize iterations and improvements. Each iteration will provide more feedback with more data points that can be used for more meaningful analysis. Without the new data points, more analysis will become more and more speculative.

Examples and signs

Spending months or even years deciding on a project’s requirements, a new UI, or a database design.

The tricky part

It can be tricky to know when to move from planning, requirement gathering and design, to implementation and testing.

tl;dr

Prefer iterating to over-analyzing and speculation.

4   God Class

Simple is better than complex.Tim Peters, The Zen of Python

What is it?

Classes that control many other classes and have many dependencies and lots of responsibilities.

Why it’s bad

God classes tend to grow to the point of becoming maintenance nightmares ― because they violate the single-responsibility principle, they are hard to unit-test, debug, and document.

How to avoid it

Avoid having classes turn into God classes by breaking up the responsibilities into smaller classes with a single clearly-defined, unit-tested, and documented responsibility. Also see “Fear of Adding Classes” below.

Examples and signs

Look for class names containing “manager”, “controller”, “driver”, “system”, or “engine”. Be suspicious of classes that import or depend on many other classes, control too many other classes, or have many methods performing unrelated tasks.

God classes know about too many classes and/or control too many.

The tricky part

As projects age and requirements and the number of engineers grow, small and well-intentioned classes turn into God classes slowly. Refactoring such classes can become a significant task.

tl;dr

Avoid large classes with too many responsibilities and dependencies.

5   Fear of Adding Classes

Sparse is better than dense.Tim Peters, The Zen of Python

What is it?

Belief that more classes necessarily make designs more complicated, leading to a fear of adding more classes or breaking large classes into several smaller classes.

Why it’s bad

Adding classes can help reduce complexity significantly. Picture a big tangled ball of yarns. When untangled, you will have several separated yarns instead. Similarly, several simple, easy-to-maintain and easy-to-document classes are much preferable to a single large and complex class with many responsibilities (see the God Class anti-pattern above).

Tangled ball of yarn

A tangled ball of yarn. Large classes have a tendency to turn into the software equivalent of this. (Photo by absolut_feli on Flickr)

How to avoid it

Be aware of when additional classes can simplify the design and decouple unnecessarily coupled parts of your code.

Examples and signs

As an easy example consider the following:

class Shape:
    def __init__(self, shape_type, *args):
        self.shape_type = shape_type
        self.args = args

    def draw(self):
        if self.shape_type == "circle":
            center = self.args[0]
            radius = self.args[1]
            # Draw a circle...
        elif self.shape_type == "rectangle":
            pos = self.args[0]
            width = self.args[1]
            height = self.args[2]
            # Draw rectangle...

Now compare it with the following:

class Shape:
    def draw(self):
        raise NotImplemented("Subclasses of Shape should implement method 'draw'.")

class Circle(Shape):
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius

    def draw(self):
        # Draw a circle...

class Rectangle(Shape):
    def __init__(self, pos, width, height):
        self.pos = pos
        self.width = width
        self.height = height

    def draw(self):
        # Draw a rectangle...

Of course, this is an obvious example, but it illustrates the point: larger classes with conditional or complicated logic in them can, and often should, be broken down into simpler classes. The resulting code will have more classes but will be simpler.

The tricky part

Adding classes is not a magic bullet. Simplifying the design by breaking up large classes requires thoughtful analysis of the responsibilities and requirements.

tl;dr

More classes are not necessarily a sign of bad design.

6   Inner-platform Effect

Those who do not understand Unix are condemned to reinvent it, poorly.Henry Spencer

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.Greenspun’s tenth rule

What is it?

The tendency for complex software systems to re-implement features of the platform they run in or the programming language they are implemented in, usually poorly.

Why it’s bad

Platform-level tasks such as job scheduling and disk cache buffers are not easy to get right. Poorly designed solutions are prone to introduce bottlenecks and bugs, especially as the system scales up. And recreating alternative language constructs to achieve what is already possible in the language leads to difficult to read code and a steeper learning curve for anyone new to the code base. It can also limit the usefulness of refactoring and code analysis tools.

How to avoid it

Learn to use the platform or features provided by your OS or platform instead. Avoid the temptation to create language constructs that rival existing constructs (especially if it’s because you are not used to a new language and miss your old language’s features).

Examples and signs

Using your MySQL database as a job queue. Reimplementing your own disk buffer cache mechanism instead of relying on your OS’s. Writing a task scheduler for your web-server in PHP. Defining macros in C to allow for Python-like language constructs.

The tricky part

In very rare cases, it might be necessary re-implement parts of the platform (JVM, Firefox, Chrome, etc.).

tl;dr

Avoid re-inventing what your OS or development platform already does well.

7   Magic Numbers and Strings

Explicit is better than implicit.Tim Peters, The Zen of Python

What is it?

Using unnamed numbers or string literals instead of named constants in code.

Why it’s bad

The main problem is that the semantics of the number or string literal is partially or completely hidden without a descriptive name or another form of annotation. This makes understanding the code harder, and if it becomes necessary to change the constant, search and replace or other refactoring tools can introduce subtle bugs. Consider the following piece of code:

def create_main_window():
    window = Window(600, 600)
    # etc...

What are the two numbers there? Assume the first is window width and the second in window height. If it ever becomes necessary to change the width to 800 instead, a search and replace would be dangerous since it would change the height in this case too, and perhaps other occurrences of the number 600 in the code base.

String literals might seem less prone to these issues but having unnamed string literals in code makes internationalization harder, and can introduce similar issues to do with instances of the same literal having different semantics. For example, homonyms in English can cause a similar issue with search and replace; consider two occurrences of “point”, one in which it refers to a noun (as in “she has a point”) and the other as a verb (as in “to point out the differences…”). Replacing such string literals with a string retrieval mechanism that allows you to clearly indicate the semantics can help distinguish these two cases, and will also come in handy when you send the strings for translation.

How to avoid it

Use named constants, resource retrieval methods, or annotations.

Examples and signs

Simple example is shown above. This particular anti-pattern is very easy to detect (except for a few tricky cases mentioned below.)

The tricky part

There is a narrow grey area where it can be hard to tell if certain numbers are magic numbers or not. For example the number 0 for languages with zero-based indexing. Other examples are use of 100 to calculate percentages, 2 to check for parity, etc.

tl;dr

Avoid having unexplained and unnamed numbers and string literals in code.

8   Management by Numbers

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.Bill Gates

What is it?

Strict reliance on numbers for decision making.

Why it’s bad

Numbers are great. The main strategy to avoid the first two anti-patterns mentioned in this article (premature optimization and bikeshedding) was to profile or do A/B testing to get some measurements that can help you optimize or decide based on numbers instead of speculating. However, blind reliance on numbers can be dangerous. For example, numbers tend to outlive the models in which they were meaningful, or the models become outdated and no longer accurately represent reality. This can lead to poor decisions, especially if they are fully automated ― see automation bias.

Pryzbylewski from the show "The Wire" teaching a classroom.

Do you find yourself commiserating with Pryzbylewski from the HBO show The Wire, Season 4?

Another issue with reliance on numbers for determining (and not merely informing) decisions is that the measurement processes can be manipulated over time to achieve the desired numbers instead ― see observer-expectancy effect. Grade inflation is an example of this. The HBO show The Wire (which, by the way, if you haven’t seen, you must!) does an excellent job of portraying this issue of reliance on numbers, by showing how the police department and later the education system have replaced meaningful goals with a game of numbers. Or if you prefer charts, the following one showing the distribution of scores on a test with a passing score of 30%, illustrates the point perfectly.

Score distribution of the 2013 high school exit exam in Poland with passing score of 30%.

Score distribution of the high school exit exam in Poland with passing score of 30%. Source in Polish, and the the Reddit post that I first saw this in.

How to avoid it

Use measurements and numbers wisely, not blindly.

Examples and signs

Using only lines of code, number of commits, etc. to judge the effectiveness of programmers. Measuring employee contribution by the numbers of hours they spend at their desks.

The tricky part

The larger the scale of operations, the higher the number of decisions that will need to be made, and this means automation and blind reliance on numbers for decisions begins to creep into the processes.

tl;dr

Use numbers to inform your decisions, not determine them.

9   Useless (Poltergeist) Classes

It seems that perfection is attained, not when there is nothing more to add, but when there is nothing more to take away.Antoine de Saint Exupéry

What is it?

Useless classes with no real responsibility of their own, often used to just invoke methods in another class or add an unneeded layer of abstraction.

Why it’s bad

Poltergeist classes add complexity, extra code to maintain and test, and make the code less readable—the reader first needs to realize what the poltergeist does, which is often almost nothing, and then train herself to mentally replace uses of the poltergeist with the class that actually handles the responsibility.

How to avoid it

Don’t write useless classes, or refactor to get rid of them. Jack Diederich has a great talk titled Stop Writing Classes that is related to this anti-pattern.

Examples and signs

A couple of years ago, while working on my master’s degree, I was a teaching assistant for a first-year Java programming course. For one of the labs, I was given the lab material which was to be on the topic of stacks and using linked lists to implement them. I was also given the reference “solution”. This is the solution Java file I was given, almost verbatim (I removed the comments to save some space):

import java.util.EmptyStackException;
import java.util.LinkedList;

public class LabStack<T> {
    private LinkedList<T> list;

    public LabStack() {
        list = new LinkedList<T>();
    }

    public boolean empty() {
        return list.isEmpty();
    }

    public T peek() throws EmptyStackException {
        if (list.isEmpty()) {
            throw new EmptyStackException();
        }
        return list.peek();
    }

    public T pop() throws EmptyStackException {
        if (list.isEmpty()) {
            throw new EmptyStackException();
        }
        return list.pop();
    }

    public void push(T element) {
        list.push(element);
    }

    public int size() {
        return list.size();
    }

    public void makeEmpty() {
        list.clear();
    }

    public String toString() {
        return list.toString();
    }
}

You can only imagine my confusion looking at the reference solution, trying to figure what the point of the LabStack class was, and what the students were supposed to learn from the utterly pointless exercise of writing it. In case it’s not painfully obvious what’s wrong with the class, it’s that it does absolutely nothing! It simply passes calls through to the LinkedList object it instantiates. The class changes the names of a couple of methods (e.g. makeEmpty instead of the commonly used clear), which will only lead to user confusion. The error checking logic is completely unnecessary since the methods in LinkedListalready do the same (but throw a different exception, NoSuchElementException, yet another possible source of confusion). To this day, I can’t imagine what was going through the authors’ minds when they came up with this lab material. Anytime you see classes that do anything similar to the above, reconsider whether they are really needed or not.

Update (May 23rd, 2015): There were interesting discussions over whether the LabStack class example above is a good example or not on Hacker News as well below in the comments. To clarify, I picked this class as a simple example for two reasons: firstly, in the context of teaching students about stacks, it is (almost) completely useless; and secondly, it adds unnecessary and duplicated code with the error-handling code that is already handled by LinkedList. I would agree that in other contexts, such classes can be useful but even in those cases, duplicating the error checking and throwing a semi-deprecated exception instead of the standard one and renaming methods to less-commonly-used names would be bad practice.

The tricky part

The advice here at first glance looks to be in direct contradiction of the advice in “Fear of Adding Classes”. It’s important to know when classes perform a valuable role and simplify the design, instead of uselessly increasing complexity with no added benefit.

tl;dr

Avoid classes with no real responsibility.