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.
An IDE is no substitute for an Intelligent Developer.
Understanfing Jsf life cycle and its internal workings can be challenging to a total newbie using the framework.
How do you explain a text you put in an input field in jsf veiw turning up four times in you database with a single click on commandButton? Yea. Thats how challenging it can be. One rule of the thumb I've leaned so far using the framework to build application is:
Managed beans are no place for business logic! Again, developers pass the word 'business logic' around all the time to the confusion of newbies. You really won't understand until you've read books on developing web application with java ee and actually building one. That s the only way you'd know for sure how easy or hard it is developing web application with jsf compared to other frameworks. For me, this is only my first step into becoming java/ee expert like Tim *wink*
DON'T do Important Things in the setter and getter methods. JSF can call them up to about 6 times in a single request/response cycle.
The getter/setter methods are supposed to be idempotent. That is, not matter how many times you call them, the net result should be the same. In a related issue, you shouldn't do any heavy work in them because you'll multiply the resource consumption.
I have a "just-in-time" trick that I like to use for fetching database records. There's really no good "fetch point" in a JSF backing bean. The constructor is only invoked when the bean is first created - which can be useless for a session object that's repeatedly used. Plus not everything has yet been injected at that point. Post-construct methods aren't much better. So what I generally do is fetch data on the first property-get call and cache it for subsequent calls. That makes the method idempotent and avoids duplicated work. And, conveniently, if I want to get the updated value, I can invalidate the cache at any time and the new data will be fetched on the next "get" request.
Backing Beans are UI Models. They should be distinguished from Persistence Domain Models. Both types of objects are POJOs, but JSF is not well-suited for working directly with Domain Entity Objects, because JSF has no inherent persistence abilities of its own, and in particular supports neither finder functionality nor arguments in object constructors. So it's better for JSF Model objects to manage/contain Domain Model objects than to be Domain Model objects themselves. Besides, often you need a façade for Domain objects. Many DBMS's/ORMs don't have a native boolean datatype and JSF checkboxes cannot deal with boolean-like values such as T/F, Y/N, 0/1 but require true booleans.
Technically, the action methods in backing beans are "business methods", but yes, it's a good idea to put the really serious business logic in a separate business logic bean(s). The action methods are mainly about getting the backing bean property values to/from business logic. Simple logic is OK, but when it's over half the code in the backing bean, that's a bit much. Then again, that's true of virtually any sort of MVC system.
And no, you don't write Controllers yourself in JSF. I don't care what Netbeans names its backing beans, they're still Model objects. A Controller copies values between View and Model and JSF does that for you automatically. The Controllers are built into the FacesServlet and the tag implementations. An action method isn't controller code - the values have already been updated before it's invoked. And listeners shouldn't be updating the model either (although I have been known to use then to invalidate cached values as I mentioned above).
An IDE is no substitute for an Intelligent Developer.