Firstly, use Google Chrome for development work. The development screens (and all screens) open much faster than on Mozilla or IE and you lose little. I also have the Eclipse tooling set up – the Force.com IDE – but it is perfectly possible to develop without it to start with. Here is the setup we use for version control and continuous integration:
The Force.com IDE is poor compared to Eclipse’s Java tooling so set your expectations accordingly. Refactoring is particularly frustrating as the server tooling tracks dependencies but leaves it to you to have to manually unwind them. Support for team development is questionable – see e.g. How can multiple developers efficiently work on one force.com application? But the “Force.com IDE Library” book in the help includes all the major documentation so by setting the help search scope to that book you have a single searchable reference rather than multiple PDF files to search. (But watch out for hits in the “Metadata API Developer’s Guide” which are not relevant to development inside the Force.com platform.) If you are writing anything other than a trivial application, the Eclipse IDE’s core facilities are vital for the ability to quickly navigate between all the files and search in them and keep version histories of them locally and in an external version control system.
Don’t expect to be able to “program at the speed of thought”. The normal cycle of code/compile/test is interrupted by many second delays in the compile and test steps. (In Java development you are using your local CPU and disk; with Apex you are sharing a remote server and database that is at the end of a limited bandwidth Internet connection.) If you need to debug the delays go up to many minutes given the very limited nature of the debugging tools. These delays really slow down the development of any intricate logic.
Apex is a small, closed language that has first-class built-in support for database operations but is more limited for general purpose programming. Its capabilities are roughly those of an embedded language in a database such as Oracle’s PL/SQL and Microsoft’s Transact SQL. But it does support some object oriented concepts such as interfaces and classes albeit with awkward mandatory keywords such as virtual and override. Watch out for how protected methods are handled – its not what you would expect. Apex has Java-like syntax (with some major omissions) and only has a tiny number of system classes.
The weaknesses of the programming language, class libraries, persistence framework, versioning model, web services facilities, test automation support, development tools, debug tools and team development support plus the closed nature of the platform mean the “5x faster at 1/2 the cost” does not apply where significant amounts of Apex code has to be written.
When your frustration level with the platform is rising (try this for more fuel), just remember that there should be some elegant and valuable platform features that you are benefiting from (see this for some candidates) and/or significant commercial benefit as a compensation. If there is not then you should probably be using a different platform…
Dan Appleman’s 2012 Advanced Apex Programming book is a “must read” if you are investigating the platform or starting to use it because he focusses on the unique aspects of the overall platform rather than just Apex language syntax.
Specific things that are different from Java are:
- No packages (except when code is packaged for deployment).
- Code is case insensitive so it is very easy to get name clashes e.g. a variable “map” clashing with the type “Map”. The reserved words in the language are reserved largely irrespective of the context they are used in. You end up adding arbitrary name variations to work around this.
- Single quote string delimiter, Integer rather than int, no char type, missing methods. One of the first algorithms I did in Apex I wrote first in Java and then ported the code including the unit tests. The port took half as long again as the original development.
- The governor limits on how much code can be run per request mean that there are restrictions on what sort of algorithms you can build. For example, at one point I had an expression interpreter written in Apex to allow end-users to add expressions. But this code needed to run both from controllers and from triggers and in the trigger case the 20,000 statement limit could be hit so in the end I had to remove it. (In Spring ’11 the lower limits for triggers were removed so now its a 200,000 statement limit everywhere.) More recently a colleague has been hitting the 200,000 statement limit in their core logic with a customer who is adding many years of historical data. The rational fix would be to buy a higher limit for that customer but that option is not available. Given the gains in Apex performance that have been made – from My laptop is 300 times faster to My laptop is 10 times faster – it is disappointing that this governor limit has not been raised.
- Building frameworks and abstractions is perilous: gains in code cleanliness are worthless if they result in the application hitting any of the many governor limit brick walls. (But do see Applying Enterprise Application Design Patterns on Force.com for some inspiration on how to tailor to the platform if you want to go that route.)
- Perhaps related to both the governor limits and the lack of any lightweight library mechanism there is a negligible amount of useful shared library code available.
- Debugging tools are improving but are limited. To get logging output recorded this must be turned on for your User via Setup -> Monitoring -> Debug Logs -> New. After 20 logs it turns itself off automatically and you have to go and turn it on again. Or the “Developer Console” window can be used. The logging is very noisy. Note that by default logging output from an installed managed package is very much reduced presumably to avoid exposing private internal workings but full output can be turned on via a support request.
- Little unit testing infrastructure beyond a few assert methods and an @isTest annotation so Test Driven Development is harder than it should be. Note that managed packages must have 75% code coverage. There is a helpful code coverage display in the web UI when you use the “Run Test” button; click on the code coverage percentage value to see which lines were covered.
- No switch statement.
- Enum support is very basic.
- No char type – have to substring out a single character string to steps through strings.
- toString isn’t marked as virtual so can’t be overriden.
- String format methods are limited (to Java’s MessageFormat.format accessed via Apex’s String.format) and parse methods are not provided.
- Numeric type conversion is different; for example “3.14 instanceof Double” returns true as does “3.14 instanceof Decimal”.
- Surprisingly Instanceof null is true in Apex.
- A List is not an Object. So you can’t return lists of objects from methods of return type Object.
- You can’t throw Exception, you have to sub-class it. You can’t add constructors to your exception. Exception is not the superclass of exceptions such as System.DMLException so care is needed in coding the types in the catch of try/catch statements. Exceptions often reported without stack traces; no API to produce a stack trace. ”Classes Extending Exception must have a name ending in ‘Exception’”.
- By default, exceptions emailed from managed packages have no stack trace information, you just get the message. So when you go into production and so are working more blindly as far as knowing what your users are doing, you lose the most important information about the exception. However you can get the full output turned on via a support request, so if the problem can be reproduced that is the way to go.
- Note that the method Exception.getStackTraceString was added in Spring ’11 that will be helpful when logging and re-throwing exceptions.
- Exception handling has a bug (that presumably can’t be fixed for backward compatibility reasons) where if the return statement causes the exception a catch statement won’t catch the exception – yuk. See Exceptions slipping through try-catch.
- No anonymous inner classes. Can only nest inner classes one level deep. Inner classes cannot reference fields of the containing class – they are like static inner class in Java. So its really just playing with the name of a class – qualifying it by the containing class name.
- Static members only have request scope so can’t be used for e.g. caching across requests.
- The architecture keeps the server-side code stateless and so offers no mechanisms like Java’s HttpSession interface. State can be kept between requests in fields of a single controller with that state serialized/deserialized to/from a “com.salesforce.visualforce.ViewState” hidden form field in the page. But this really only facilitates page validation and wizards where a single controller is shared by multiple pages. Only the database can hold state that is used by separate pages/controllers. Adding custom fields to the User object allows “per user” information to be held to somewhat simulate “per session” information.
- No support for using generics in your own class definitions.
- Only one type of List, Set and Map;
only sorting by the natural order of primitive typesthere is now a Comparable mechanism. No Collection super class, so have to explicitly use List or Set. But note this neat initialization syntax.
- The == operator tests for value equality and the === operator tests for reference (memory location) equality (on SObjects and collections). Check the documentation for the details such as == on strings being case insensitive.
- Very limited reflection and the describe (meta-data) calls hit governor limits very quickly. There was no way to create an instance of a normal Apex class by name but there now is – see Available: the ability to create an instance of an Apex class from the class name. So if a managed package defines an interface that some customizing Apex code implements the managed package can instantiate the customizing Apex code. This opens the door on the conventional pattern of providing extension points and so supports the Open/Closed Principle. See Wanted: the ability to create an instance of an Apex class from the class name for a bit more explanation.
- You can create an instance of an SObject from its SObjectType but you can’t create the correctly typed collection instance that is required by the insert/update/delete calls to deal with collections of objects and so help stay within governor limits. and can now make insert/update/delete calls on collections of those SObjects – see Salesforce: Instantiating an SObject Dynamically at Run-time including the comments.
- Code that is illegal in a context can be added. For example, a call to ApexPages.addMessage will compile in a trigger but generates this exception at runtime “ApexPages.addMessage can only be called from a Visualforce page”.
- The patterns needed to minimize governor-limit problems obfuscate the logic you are implementing in both the product code and test cases. So instead of the code mirroring the required business logic, it consists of a series of set oriented operations with business logic fragments here and there.
- If you need to make web service callouts, experiment with WSDL2Apex to confirm that it can handle the target WSDL. See When “Generate from WSDL” fails for the frequent cases when it can’t.
- To test SOSL (the index based query language) you have to set fake results using Test.setFixedSearchResults – see TestMethod Problem.
- Unit tests can’t make web service call outs
, but the code generated by WSDL2Apex doesn’t provide any extension points to fake those call outs. So you end up with zero code coverage for those generated classes or have to hack the generated code. There is now a way to test web service callouts – see Testing Apex Callouts using HttpCalloutMock.
- No IO available except HTTP access to external resources that have to be nominated. No way to find the URL of a static resource (e.g. a resource called “A” is at “/resource/1264356232000/A” – according the Visualforce URLFOR) or open it in Apex code (to do e.g. programmatic content merging). Correction: there is this technique that allows file or zip file content to be read via a PageReference if a prefix of “/resource” is added.
- No way to sleep in Apex code making any kind of polling in Apex hard to accomplish. (Also if you have logic that you want to test that makes use of the LastModifiedDate on an SObject that has a granularity of 1 second, the lack of a sleep call makes this logic virtually impossible to test.)
- Triggers are the normal way to add behavior that applies however an SObject is updated. But if you need to make a web service call out as part of that behavior (e.g. external validation) you will be out of luck because that the call has to be made asynchronous through the @future annotation: you cannot see the response within the trigger.
And a few gotchas on SObjects:
- An SObject class is just a data holder; you can’t add behavior in the form of additional methods or method overrides.
- The object/relational mapping part of persistence that is a base feature in other platforms (JPA in Java, Active Record in Ruby on Rails, GORM in Grails) is entirely missing.
- There is no caching or automatic population. Just the fields and objects that you fill in with explicit SOQL calls are populated.
- It is hard to write re-usable Apex code that manipulates SObjects because an implicit part of the contract ends up being which fields are and are not populated. (See section below.)
- Formula fields are calculated in the persistence layer. So if you are doing a computation that references a formula field before the changes have been persisted the changes will be missing. In a computation of any complexity across objects that have formula fields you end up with Apex code duplicating the formulas – an annoying violation of the DRY principle. And there is no tooling to do things like generate delegate methods to hide the ugliness behind a facade class.
- Similarly trigger changes are done in the persistence layer. This means for example that objects need to be re-queried in unit tests for triggers.
Similarly rounding is done in the persistence layer. So if calculated intermediate values are assigned to a field they keep their full scale until persisted and re-queried. If the calculation requires that the rounded values are always used then explicit setScale calls must be made.Rounding (to the number of decimal places defined for a field) is done in the UI layer only. See Glenn Weinstein’s Salesforce and Decimal Places for a good explanation. Explicit setScale calls are needed to keep what is in the database consistent with what the user sees in the UI where values have been calculated.
- In a relationship, in the child object the field PPPP__c is the Id of the parent object and PPPP__r is a parent object reference where “PPPP” is the type of the parent object. In the parent object, CCCCs__r (note the plural that is typically used) is a list of references to the child objects where “CCCC” is the type of the child object. All these fields are populated if the data is fetched by a SOQL query that pulls both the parent and child objects, allowing foreach loops over the CCCCs__r list or access to the parent from the child via the PPPP__r reference. But it is not possible to construct object graphs that have these fields set through Apex code. For example, adding an object to the CCCCs__r list silently does nothing. This means that for unit tests objects have to be inserted and then queried back rather than just constructed resulting in ugly duplication. Also in triggers the CCCCs__r and PPPP__r fields are typically (always?) null.
- There is a nice “Track Field History” feature that for custom object Abc__c automatically creates an Abc__History object and automatically adds entries for changes to nominated fields. So in theory you can add logic to your application based on the history object. But be aware that there is no way to then unit test this logic – see Impossible to test code covering __History tables - meaning that in practice it would be foolish to add such logic.
And on SOQL (the SQL-like query language):
- Coming from a SQL background SOQL takes a bit of getting used to. This article From SQL to SOQL: Force.com for .Net Developers may help. This article A Deeper look at SOQL and Relationship Queries on Force.com is a big help for how to construct queries across multiple objects and queries that return data from multiple objects at the same time. Here is a simple example.
- But the SOQL “where” syntax is limited. For example, you can perform logical operations on the data fields but cannot on the bind variables.
- SOQL is checked at compile time which is cool. But “select *” is not supported so it is pretty common to hit the “SObject row was retrieved via SOQL without querying the requested field” error where a field has not been named in the SOQL but has been referenced in a Visualforce page or some Apex code. Having to name the fields is an annoying violation of the DRY principle in a small scale application. But in a larger scale application it is a major block to creating re-usable code, because the fixed set of fields loaded is a hidden part of the contract of a method. You can get all the field names via meta-data calls but they are subject to a governor limit of a maximum of 10 object types which is not that hard to hit in a normal object model. And taking such an approach of building “dynamic SOQL” strings means the resulting SOQL is no longer checked at compile-time.
And on development tools:
- The development tools are largely duplicated in web-based form and Eclipse-based form and there is only one company developing those tools so improvements are slow to come.
- The Eclipse-based tools don’t appear to have had any investment for quite some time. Examples of basic weaknesses are that no Visualforce errors are reported (just a generic “File saved locally, not to server” message is displayed), that the Apex outline view has no method ordering or visibility options and that the test runner has decidedly unhelpful result panes. But you do benefit from the ever improving Eclipse platform itself.
- The web-based tools work best on projects that have a small numbers of simple files. With larger volumes, the inability to easily search and work across many files at once plus the many seconds it takes to even find and open a file makes for a painful experience.
- Both sets of tools are subject to server delays of many seconds before routine things like code syntax errors are reported.
When you hit something unexpected the first place to search to find a solution is Developer Force. Known bugs judged by Salesforce to be of general interest are now also publicly posted at Known Issues. There are many blogs you can learn from. For example here is a great posting from Jeff Douglas – Force.com Programming Best Practices.