Writing enormous amounts of extra "checking" code is pretty much needed unfortunately. The checking code is usually enough to help, as you can get the changes that break your code by printing what made the code fail where it failed. This is useful to the user if they gave to program bad input. Failing with decent error messages during checks is the easiest way to debug bad input.
One of the ways to validate data is to have a builder. You give the builder the pieces of data you have and then have it build an object consisting of that data. The factory can produce fuzzy logic (Yakk's idea), or it can throw an error if any data is missing when you tell it to build. You can also add methods to the builder to check if the data was fuzzy generated or is valid. Each data feed into the builder can also check that the data is valid on input, and throw a helpful error.
Anticipating bad input is one way to deal with it like you say in your question. You can write write code that checks if data should be equal (simple example being Hello and hello being the same words despite capitalism). This is really something to wait for an error for except simple examples. If the user really needs you to support a format, you can get the error message with details if you wrote good checking code. Then you can add support for the format they want. This can be harder said than done.
If you do need to add support, using a base interface can help if you need to change a lot of code. So say one customer has a different csv format, you can create code on top of the original interface that is labelled with that customer. So with your csv example, say one customer uses ;s instead on ,s. The base interface would deal with that and you can label the code on tope as semicolonSeperatedValues or something like that. This does take some thought as to what is needed in the base interface. This comes with a disadvantage of a lot of refactoring if there are some poor design choices early on, but it can help prevent duplicate code and bloated program files.
You can also ask the users of your software what their format is. Make sure if there is an error to print the error with the formatting that produced the error so they can fix the input; they can also give you a decent error message that helps you write more robust code.
As far as decent error messaging goes, as long as the error has helpful information and doesn't give the user an ugly crash or exception, you are good. Going with the csv example, if the user has a bad file, you should display an error that says what file, what line, and why that line is bad. Also, make sure not to change the state of data you are reporting the error on, otherwise you will be left with a potentially very obscure bug, and could confuse the user.
Try to avoid creating exceptions. An example in Java being a NullPointerException. You can pass null around, but unless you are checking for null everywhere it is getting passed around, eventually a NullPointerException will get thrown. In Java, they have a way to avoid this by using empty containers. If a method you are using throws an exception, you want to write code that will never trigger that exception.
Also, very important, do not ignore exceptions as a way of error handling. You will cover up what could potentially cause errors far away from their source.
Minimizing variable scopes also helps with errors. Having a global variable that multiple programs depend on is a good way to introduce a bug. Giving each program their own variable is much safer. Even safer is method local variables.
In multithreading environments, using immutable classes helps avoid a lot of potential headaches. In fact, multithreading is best avoided unless the performance is needed, because debugging errors is a lot harder in a multithreaded environment.
You want to give the user only as much control as they need. You want to give the user as little control as they need. This will prevent a user from messing up and getting frustrated at you for what they perceive as being your fault.
Using programs designed to do stuff for you is also a good way to avoid errors. A simple example being a for each loop in Java vs a normal loop where it is much easier to get an IndexOutOfBoundsException.
Getting familiar with the programming language you are using is also a great way to avoid errors. Find some reading material and exercises and do them.
Also, in multithreading environments, make sure to synchronize data. This is a complex subject in and of itself and has books written on it. Once again limit the control of the user. They absolutely should not be able to mutate synchronized data while it is being synchronized.
I have tried to make this list as general as possible, but the ideas are from reading Effective Java.