Here is the setup we use for version control and continuous integration:
Development tools are poor compared to say 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?
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. Winter ’16 has Debug Apex Code in Eclipse (Generally Available) but at a high price and subject to approval by Salesforce so it looks like few people will get to use it.
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 or guess what is the top of the list when you click the “Most Dreaded” tab in this 2015 developer survey), 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 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. Update: this governor limit has changed – see Winter ’14 – no more code statement limit.
- Visualforce becomes the performance bottleneck sooner than you might expect – see (the lack of) Advice on speeding up Visualforce pages.
- 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.
- 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 in API versions before Winter ’15.
- 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'”. See e.g. Leaky Abstractions: Apex Exception Types to get a sense of the weirdness.
- 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.
- When a large amount of processing needs to be done you have to move to using Asynchronous Processing in Force.com. Writing tests then becomes a challenge as asynchronous processing in the test context is very limited.
- 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.
- Governor limits apply to SOQL too. For example, if your application contains “select Count() from Contact”, it will blow up when an org has more than 50,000 Contacts with a “Too many query rows: 50001” which is a little surprising given only one number is being returned.
And on development tools:
- The Eclipse-based Force.com IDE was in maintenance mode for several years. Recently (2014) the approach has changed to an open source model where community contributions are accepted (see forcedotcom/idecore). But this so far has had effects such as reducing the number of releases not increasing them, and removing the auto-completion that was available because eventually better auto-completion will be available. One step forward but two steps back…
- Salesforce’s aim is to provide APIs and get others to create tools that use them. There is a list of tools here Force.com Tools and Toolkits. MavensMate is frequently recommended and a slam dunk choice if you already like Sublime Text. Illuminated Cloud that is based on Intellij looks promising.
- The web-based tools (e.g. the Developer Console) 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.
- Tools are subject to server delays of multiple seconds before routine things like code syntax errors are reported. (Salesforce to date have not published the formal syntax of Apex making it hard for tool writers to build accurate parsers that can report all errors independently of the server.)
When you hit something unexpected the first places to search to find a solution are Salesforce Stack Exchange and 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.