Well, based on questions asked in this forum, I'd say that these are some common issues:
1. Inversion of Control. People traditionally have learned programming as a active task - you "get out and do things". In IoC, it's Good things Come to He Who Waits. If you're learned Spring, CDI, or one of the other injection frameworks, this may not be much of a problem, but for people who haven't, it's kind of mentally turning one's mindset inside out.
2. Accessing JSF internals. Another thing most frameworks are big into is APIs. JSF is designed to be built on POJOs. As a general rule, any time you have to use a class in the javax.faces package subtree other than the DataModel or SelectItem classes, there's a good chance that you're doing something wrong. Or at least the hard way. In particular, reaching through the FacesInstance to get raw HTTP request, response, and session information is a Red Alert.
Having said that, there are a few HTTP-specific things that apps often need: the RemoteUser id, the ability to check isUserInRole, stuff like that. I wrap all those functions in a FacesUtils class of my own devising so that there aren't chunks of JSF internals splattered all over my backing beans. It also makes it easier to mock out for
testing purposes.
3. Logic in the View Template. A common offense is that people like to code complex EL or even use JSTL
on their View Templates. There are 2 problems with this. First, you cannot easily run View-based logic through a debugger. Secondly, by splitting logic between View and Model, you present maintainers with a "Treasure Hunt" scenario where they have to bounce back and forth between bean and view definitions to get an idea of what's really going on. It's far easier to put the logic in the backing bean where an ordinary
Java debugger can run against it and you'll have "one-stop shopping" for your logic.
In particular, I discourage invoking methods or passing parameters from Views to backing beans. JSF is supposed to handle the "parameters" automatically using backing bean updates (with validation). Here again, the more work you do, the more likelihood you're doing it wrong.
4. Use of raw HTML in view templates. In a few cases, it's unavoidable, but where possible, I recommend using only JSF tags, not HTML tags. I realize it can be awkward - making spanning table columns and "div"s using datagrids can be cumbersome - but it immunizes the View Template from a particular presentation method. Granted, virtually all View Templates get rendered to HTML, but if you plugged in a renderer that output a PDF instead, you'd end up with a printable document splattered with literal HTML tag text in the middle of the content.
6. Scope. Request Scope is Almost Useless. End of story.
7. URL tracking. This one drives everyone crazy. "Why doesn't the URL reflect the page I'm on?" Because in JSF, the URL is more of a handle to the session than it is an explict resource locator. You can force tracking, but it will cost you in overhead.
I'll add one more that came in with JSF2:
In JSF2, dataTables and selects were permitted to directly reference POJO objects in their value attributes. This can be convenient, but you lose functionality when you do that. For selectItems, it chooses the name/value values of the generated OPTION HTML tags for you. For dataTables, an implicit DataModel object is constructed for you, However, by not explicitly constructing your own DataModel, the action processor doesn't have an object to invoke the getRowData and getRowId methods on in order to determine which row's action control was clicked on. People try and work around that by making parameterized calls. Like I said, don't make parameterized calls from the View. The DataModel is much simpler to work with - it's just a wrapper that adds JSF support properties to the underlying POJO collection.