If you're comparing to Python (which is stable but hella slow) or Ruby (which tends to be both unstable and slow), GHC stacks up pretty well. By "production systems", I was mostly referring to the sort of things that are normally written in Java or C++ - Google-scale server farms, or financial transaction systems.

You really want predictable execution behavior in those systems. You want to be able to look at a graph of runtime parameters - memory usage, or server load, or QPS, or 95th percentile latency - and see that it's stable, and not have surprises like a massive spike that then shows up as user-visible outages. Because getting paged really, really sucks, and losing money because your customers can't access the system sucks even more. "Kill and restart the process every now and then" is not robustness.

If your intention is to play around with some prototypes and see what you can build, Haskell on the backend and Python on the frontend is a fine choice. Your biggest issue (other than the learning curve of Haskell) will be finding libraries that you can leverage, and Haskell has one of the nicest C FFIs out there. Other than that, here're assorted other little annoyances I've run into.

1. Positional data structures (tuples, "naked" [non-record, no accessor] ADTs, functions with long parameter lists) really suck. You will eventually want to stick another bit of data in there, and then you have to update all the usage sites with new pattern bindings. As immature as the record system is, take the time to use it from the beginning; it'll save you pain later on.

This is a very common pattern in C++ as well; many experienced API designers I know will give all their methods the signature of "bool someMethod(const MethodInput& input, MethodOutput* output)", where MethodInput and MethodOutput are structs. At Google, we often use protobufs as the structs; that gives you a bunch of other benefits, as you can instantly print them out for debugging, and you can log them efficiently, and it becomes trivial to turn the API into an RPC service if it outgrows a single process.

2. Don't be afraid to use type inference; oftentimes, the compiler can infer a more general type than you can write down. If you do write down the types, be liberal with your use of type synonyms; you will probably end up changing those types later, and it sucks to have to visit a few dozen functions with a search & replace. "newtype" is very often worth it, if just for the added typechecking.

3. Point-free style is cool, but I've never been able to look at point-free code I wrote a month ago and understand what it was doing. Don't do this outside of very specific cases where the point-free version has some semantic meaning (for example, you're treating a partially-applied function as an object that does something. If you wouldn't define a closure for it in Python or JavaScript, don't make it point-free in Haskell).

4. Haskell module patterns are much more similar to C than C++ or Java. You typically want a clear divide between "data" modules (that define fundamental dataypes) and "code" modules (which define functions and algorithms acting on those datatypes). Trying to use the OOP-style, "one class per file" approach is very frustrating in Haskell.

5. The "mathematician's naming convention" (all single-character variable names, with "s" as a plural to indicate a list) is just as bad an idea in Haskell as it is in C. Go give your variables some meaningful names and ignore all the tutorials that have "xs" and "ps" and "qs".

6. Very often, you'll want to define a monad for your code from the get-go (you can even just typedef the IdentityMonad to start out with) and write your code in a monadic style from the beginning. You will want to add error-handling and probably state and logging at some point in the future. If you start out with a typedef'd monad type, you can just change the typedef to some monad transformer stack as you need to.