The main question is a bit tricky. The glib answer for how much abstraction is "not too much, but not too little". Only slightly more seriously, it's something that you really have to work out for yourself as it depends on a lot of factors.
A good first step would be to create a "basenode" configuration (a role and environment attributes) have all your nodes managed by Chef. This may include a bunch of existing community cookbooks such as:
At this stage your apps would be configured and installed by hand on top of the base nodes.
Now you should have a better idea of how Chef works so you can start to think about what more domain-specific cookbooks would look like. Continue working bottom up and write cookbooks to install the packages required for your apps and use attributes and data bags to configure them. There may be some commonality between the 20 apps you mention (they are mostly Rails apps, for example) that you can factor out into a common set of recipes.
You make a good point about finding a medium between manual configuration and automation. One of the traps that I fell in to when starting out with Chef was to try and automate as much as possible. This ends up turning into a nightmare of corner cases and weird bugs that are fixed by restarting a service by hand or rebooting the node. Not fun to maintain and debug. These days I try to stick to the package, file and template resources as much as possible.
Having 100 roles does sound a bit over the top, but I don't know any more details about your situation. I tend to work with more generic roles like "Apache-Server" or "RabbitMQ-Server" and then use various combinations of attributes, data bags and more specific roles to personalise each node.