There are a couple things people do. One thing is to check for goog.DEBUG, which is what the Google Closure library also uses. In your ClojureScript compiler options you can add
:closure-defines {goog.DEBUG false}
for the production build, and {goog.DEBUG true} for the dev build, and then check for it in your code, (if goog.DEBUG ...)
This doesn’t stop code from being included in the final build though. You can come up with a macro scheme, although I don’t immediately have a good example. You would have to rely on something else than goog.DEBUG since macros are evaluated in the JVM (when using the default Clojure based compiler).
A more common approach that people use is to make use of separate dev/prod source paths.
and then having certain namespaces in there with different function or macro definitions based on dev or prod. You could even have a generic when-dev macro for instance
Closure will definitely remove conditional code if the conditional can be resolved at compile time. It sometimes takes a type hint to remove the cljs.core.truth_ check which CLJS injects to have proper truth semantics.
The Closure Compiler does properly recognize this and once you run this through :advanced (or even :simple) this if will the completely removed.
Macros can also work but I generally advise against them since build tools will not be aware of any trickery you do with environment variables and such. Caching will usually become very unreliable if you do this and will often mean that lein clean or equivalent is your only option for reliable build output. Not something I would want to trade.
Another option would be reader conditionals but CLJS currently does not support this. It would be pretty easy to add though.
PS: It is unfortunate that the ^boolean type hint is still required in all cases, we can definitely get rid of some. [CLJS-1439] - JIRA has been open quite a while, maybe some votes would help.