I've been using COBOL for a number of years now, dating back to my time in high school during the mid 90s. That's right, they were still teaching COBOL back in the mid 90s. Over the years, I've come across some things in COBOL that I find are downright evil.

The use or the misuse of the period
The period is innocent enough in its own right, but in COBOL, they can be your worst enemy. Let's say, for example, you're coding up a nice little IF block. For some reason, a period ends up in the middle of your block and you don't catch it. What happens now? Well, depending on what you are doing with the IF block, a number of bad things can happen. Take the following code example:

IF (DB-DELETE-SW = "Y")
    DISPLAY "DELETING DATABASE RECORDS".
    PERFORM DELETE-DATABASE-RECORDS
Oops, there goes your database. Had this been coded with a scope terminator, a compile error would have resulted, which is a bit better than the database becoming blank for an unknown reason. Other times, the effects may not be seen for some time down the road. Some code gets moved into production, and the problem code is not hit until a few months down the road. Trying to debug period related problems can be downright evil, especially when you spend a couple of hours looking for other problems and find that one little punctuation mark caused you a load of pain.

Misuse of Scope Terminators
Scope terminators aren't evil by themselves. It's when they are misused, or not used at all. There have been programs where I've gone in and wanted to shout "Use them or don't". Don't mix using them and not using them, as it can cause problems when trying to read code and debug issues. Example scope terminators: END-IF, END-READ, END-STRING. If they are used, they can make code more readable and prevent some errors, such as the period example shown above.

Not using scope terminators can result in code that is very hard to follow. An example would be a nested IF statement. Take the following example:

IF (NODE-WRITTEN-SW = "Y")
    IF (NODE-UPDATE-SW = "Y")
        DISPLAY "Node update"
    ElSE
        DISPLAY "New node"
ELSE
    DISPLAY "Write something, will ya?".
While that code is pretty readable, take a look at the same code with the terminators:

IF (NODE-WRITTEN-SW = "Y")
    IF (NODE-UPDATE-SW = "Y")
        DISPLAY "Node update"
    ElSE
        DISPLAY "New node"
    END-IF
ELSE
    DISPLAY "Write something, will ya?"
END-IF.
Looking at the terminated code, it is much easier to tell where each IF starts and ends.

Indexed Files
Indexed files in COBOL can be quite a pain, depending on how you need to use them. Creating them is easy enough, the problem comes when you need to search for a record, or read the file sequentially. In order to read a record or records, you need to perform a START on the indexed file to position the internal pointer to the record you want, then you need to perform a READ to actually read the record into memory. There's all sorts of room for error here. Mess a key up, and you miss the record you need, or you instantly get a not found condition, which is not good if you're trying to read in the entire file from a certain point.

These are three of the evil things in COBOL. I won't even go into the long, drawn-out syntax or hackish SQL support. COBOL is still in use, believe it or not, but as newer technologies rise up, its use is slowly dying.