Sessions are bad because they try to bolt on a statefull pattern onto a stateless medium. This results in several things the first being no guaranteed terminator in which you have to have a catch all pattern to clean up the trash, the catch all's are not ideal for two reasons both related to the fact that it has to be time based (relying on timing in an none timed operation usually indicates you are doing something very wrong). Anyways if you get the timeout too short you kill the users session and have to create even more workflow to get them back to where they where (if you can). If you get the timeout too long you end up eating up too many server resources. I have seen it happen time and time again as soon as an app starts to experience some load as it grows the teams first reaction is to tighten the session timeout, if a teams first course of action to a scalability issues is to tighten there session timeout, they are in real trouble if they get hit with fast growth.

Session is inherently more difficult to scale horizontally, it creates the need for memory replicator routines or homegrown solutions of syncing all sessions to a DB and replicating it on other boxes. This creates complex infrastructure dependencies that have to be accounted for when you have to scale horizontally again.

As well, developers tend to, over time, uses session as a dumping ground, if they need to carry the shopping cart to the next page they just dump the whole 10 megs of data into the session, there is no way (without setting up monitors) to enforce that session is not becoming bloated.

We had a guy one time dump a 200 meg object in session for ever user (it was 1 years availability for ever hotel in North America). He needed one days data for the confirmation and the user might adjust the dates so he just stuck it in session.

Further, session being a grab bag and global in scope means that even when an object no longer has any reference session still has a reference to it so it just sits there pollution memory. So in essence session creates all the same problems that global variables caused, global variables are considered bad practice these days because of those scoping problems. Some developers get lazy and when they do session is a big lazy enabler.

Contrast these with a stateless patterns and it becomes far more simple, users never time out, so you can pick up an operation 4 days from now, the client holds all of the information to pick up the transaction where it left off. As well since the client and server transfer state between each other scaling horizontally becomes simply an exercise of installing the app on another box and adding it to the route table on a round robin load balancing strategy.

And that is the crux of the issue, session is great when you are building an app that sees no load, so developers write an app that relies on session when they are starting out, it gets them in the mentality of using session so when it does start getting load they are already in the session mentality, so instead of taking a critical look and saying we need to get this app stateless so we can quickly scale horizontally, they stay in the session mentality and start designing solutions to replicate memory, store session in databases and other suck solutions that cure the symptoms. That is usually the beginning of a long painful process that finally end in statelessness.