I would guess that many or most developers in the CQ development space have their own set of development best practises they operate with.  This is a topic very near and dear to my heart.  It is something that I have spent significant personal time pursuing.  At the end of the day I want readable, maintainable, extensible, bug free code that is not a burden.

I have deliberately kept the list short.  These have been primarily for personal consumption, and the key idea is that if I have to refer to a tome for my "best practises"...I won't.  So I wanted them practical, reasonable, and easy to remember.  Most of these I have operated with before I even realized that it was the way I was operating.  I believe that is what I want: I don't have to think about it.

I will provide the list first, then the explanation for each.

Be practical in their application.  Applying a rule for the sake of it is likely to have the reverse effect of what these best practises are meant to accomplish.  For example, rules 2 and 5 suggest a certain organization to the developed JSP's.  If the template or component is simple enough, it may make more sense to use a single JSP.

  1. Make sure what you are building does not already exist
  2. Design / build templates and components for extension
  3. When creating templates, make sure the "main body" of content is contained within a parsys called "par".
  4. Extend something that exists rather than build new
  5. Avoid using JSP Scriptlets (<% %> and <%! %>), depend upon JSTL, EL / custom EL functions and JavaBeans
  6. Keep the number of non comment lines in a JSP to around 30
  7. Be careful about adding 3rd party API's when they don't exist OOTB
  8. Don't worry about whitespace, unless you are writing non-HTML scripts
  9. Each application should have its own "global.jsp" which in turn jsp:includes's the OOTB global.jsp
  10. Get everyone to agree upon component and template property name patterns, as well as JSP naming patterns.
  11. Make use of the FindBugs plugin, and run on OSGi bundles during development

1 - Make sure what you are building does not already exist
CQ is huge. There are 307 OSGi bundles and approximately 34 foundational components, 2 custom taglibs comprising 13 tags, a dozen or so standard JSP actions, with as many JSP Core tags, about a dozen and a half EL functions, with and many other CQ components specific to add-ons. The chance the core of what you are building already exists in one form or another is very good. Spend 15 or 30 minutes doing research before you build. Eventually the research time will be nominal due to your ever increasing awareness of what CQ has to offer, as well as what is available in your own application.

2 - Design, build templates and components for extension
There are some OOTB CQ components that were clearly not designed for extension. Look at the Geometrixx search page and use the search term triangle. There is clearly a layout within this component. However the only JSP for this component is search.jsp. This script does have some internal structure to it, but it is HUGE and confusing. I can say that each new set of eyes that look at it, will easily take someone 2-4 hours to simply understanding what the pieces are.

What if I wanted to suppress tag navigation? What about limiting which tags in tag navigation are exposed? What if I didn't want the search bar exposed here, or wanted additional markup or ordering? What if I wanted to handle pagination differently? ALL of these requirements would demand a complete rewrite of the search component.

This could have been broken out into 5 sections, each with its own JSP. Also, each section would have a "switch" in the component dialog to "disable" it from view. This organization of the search component would be one that keeps extension in mind. Each section can easily be customized by providing a new JSP for it in the extended component, while still relying upon "core" component functionality.

3 - When creating template, make sure the "main body" of content is contained within a parsys called "par"
Not all pages will contain content that you will want to reuse from another page. It is possible to construct your templates in such a way that your content cannot be shared. For this reason, it is always better to assume that content will be shared in order to prevent the inability of sharing content. The difference between the two implementations is so minute that it is very unwise to not do so.
Most or all templates will contain one or more paragraph systems to contain components. One parsys is typically considered to be more prominent on the page, therefor considered the "main body" of content. This parsys name should always be "par".
The reason for this is the "reference" component. This component allows an Author to browse the site and select a component from another page. The component will make the component appear on the referring page as though it were physically authored on that page. The caveat to this is it can only select a component on pages that contain a paragraph system named "par". If any other name is chosen, you will not be able to select component from within that parsys for inclusion using the "reference" component.
The parsys naming implementation detail is so trivial that as stated before, it is very unwise not to do so. The consequences of this are inability to reuse content without a restructuring of content to meet the "reference" component requirement. This is not something you want to have to do later on in the project cycle when you have hundreds or hundreds of thousands of pages.

4 - Extend something that exists rather than build new
Like mentioned above, there is plenty of functionality OOTB. Also, as development progresses, a growing set of new functionality will exist to depend upon. Relying on these already tested and proven components will reduce the future number of bugs, while providing a shorter delivery time.
Also, this reduces the footprint in terms of code. Extended component typically need a fraction of the total amount of code than a completely new implementation does. Reduction in code means reduction in maintenance, management and potential bugs.

5 - Avoid using JSP scriptlets (<% %> and <%! %>), depend upon JSTL, EL / custom EL functions and JavaBeans
In some cases, some amount of Scriptlet code is required and unavoidable. However, keep it to a minimum. Again looking at the search component is a good example of unreadable code. Scriptlet DOES NOT facilitate:
a) Readability
b) Testability
c) Maintainability
d) Extensibility / Reusability
The transition to JSTL/EL for the uninitiated will be slow at first. But once this is incorporated into a daily routine, not only will the aforementioned issues be addressed, developer productivity will increase.
Be practical in the application of this. If you must write Java code, and it will not be reused anywhere, and it makes no sense to write a JavaBean to handle the logic, use a scriptlet. Otherwise you will end up with is a mess of JavaBeans in your OSGi bundles, and developer thrashing to switch context between editing JSP's and JavaBean logic in OSGi projects for maintenance on a component or template. The better approach is to simply store it in the JSP using a Scriptlet, for reduction in clutter in your OSGi bundles, and developer productivity for maintenance.
Like mentioned above, there are no hard and fast rules. Again, be practical and aware of your context.

6 - Keep the number of non-comment lines in a JSP to about 30
Again referring to search.jsp, this is a good example of how long JSP's make a file unreadable, and a component or template not easily understandable. Conversely, splitting a JSP up into too many individual JSP's can have the same effect: a component or template is difficult to understand. So, there is a definite tension. However, most templates and components likely will not suffer from this pitfall. Only in the case where you are developing foundation, or highly extensible templates and components, will this tension likely come into play.
Each JSP that is added to a template or component can be thought of as an extension point. It may have some functionality that can be overridden by a child component by defining an empty JSP of the same name. Conversely, the parent JSP could be an empty JSP, providing a way for the child component to provide an implementation. In this case, the parent JSP would take care of including the JSP somewhere in the structure of the template or component. It would not be a detail the child component would have to worry about. This concept is completely similar to OO inheritance, so developers ought to be able to catch on quick.

7 - Be careful about adding 3rd party API's when they don't exist OOTB
Work needs to be done, and relying upon something that already works and is tested, is usually a good alternative. When adding non-OSGi API's to CQ, I prefer to define them as embed dependency, and proxy the functionality rather than expose the entire API. Usually in this kind of scenario, only a small portion of the API (maybe even a single class) is all that is needed. In this case it is easy enough to provide a custom proxy deferring work to the "package private" 3rd party API's.
One example of a potential problem was on CQ 5.3 (and maybe 5.4), the JAX-WS API, which is part of the standard JDK, was not exposed through the system bundle. It was possible to do this through the use of a system extension fragment bundle.
The "side effect" of this is exposing the JAX-WS API to the entire OSGi container. The problem with this would have been with upgrade to CQ 5.5, which exposed the JAX-WS API to the container. This would have caused lots of problems. These problems would likely not have been immediately obvious because the time between the deploy of the highly customized OSGi bundle to CQ, and the upgrade to CQ 5.5 would have been long enough for those in the know to forget about the customization. Also, we all know how succinctly classpath type Exceptions are reported by the JVM.
Another problem I have seen centers on deployment of OSGi bundles. One project had deployed OSGi bundles that obviously had dependencies on some of the foundational OSGi bundles provided by Apache Felix, but the effects were not immediately obvious. The only "visible" effect (and it took me 3-4 months to determine the issue) was that upon Application deploy via CQ bundles, randomly (about every 4 or 7th deploy) one of the App bundles would fail to deploy because the CQ Package Manager "disappeared".
What was happening was the deploy of App #1, which had the offending 3rd party OSGi bundle, was causing the Package Manager bundle to be recycled. As timing would have it, just at the time the next bundle was trying to be deployed, the Package Manager was not there, and failing the deploy.

8 - Don't worry about whitespace, unless you are writing non-HTML scripts
You will notice the sometimes heavy use of <% %> - with nothing but whitespace in between. The purpose of this is to "hide" newline/carriage return in a JSP. The use of this is to prevent the JSP setting the content type of the response prematurely. While this may be needed in some cases, if you are writing a JSP that produces markup, it is not.
Some front end people will complain about all of the whitespace in the source of a webpage, and you may be tempted to use the Scriptlet trick to "hide" this unnecessary whitespace. Don't. Configure the JspScriptEngineFactory and set "Trim Spaces" to true.
In the case of writing Scripts that produce JSON or other non-HTML markup, you will want to HEAVILY employ the Script trick to prevent setting the response content type prematurely.

9 - Each application should have its own "global.jsp", which in turn jsp:includes the OOTB global.jsp
Each JSP written should include your own applications global.jsp. For one, I have seen the location of the global.jsp change a couple times. While it has remained static for a couple versions now, implementing this was does not cost anything, and saves you the potential for having to update hundreds of JSP's with a deprecated global.jsp path.
We will talk more about this in the patterns section, but you will want to use this to leverage the ability to easily include: company specific Objects, String, configuration values, etc. that may be needed across your application.

10 - Get everyone to agree upon component and template property name patterns, as well as JSP naming patterns
Like variable names, property naming styles can vary wildly at developer discretion if some standard is not set. This can make property name usage difficult and confusing. Deciding ahead of time will circumvent people making up their own naming standards which may or may not be easy to use.

11 - Make use of the FindBugs plugin, and run on OSGi bundles during development
This is a very simple, unobtrusive, low effort measure that can be taken. It is guaranteed to reduce overall bug count and increase code quality.