Key Takeaways
- The article shares insights on the Salesforce development workflow, focusing on Force.com.
- It emphasizes the need for a personal developer environment and proper source control setup.
- Apache Ant plays a crucial role for managing metadata and deployment tasks.
- Developers should regularly pull the latest code from the repository to keep their environments updated.
- Adapting the workflow based on individual needs is essential for successful development on the Force.com platform.
Introduction
I started working with Force.com back in mid-2012. My goal at that time was to learn all about point-and-click, drag-and-drop, or better known as declarative development. Being a software engineer, I really wanted to get into code because that’s what interests and drives me. Regardless, I went through the Force.com Workbook for that year and picked up as much knowledge as possible. I discovered how powerful the platform is and how quick it can be to get something up and running (even in code, most boilerplate is done for you).
I didn’t touch it much after that and moved to different things at the time, mainly some fun development with ASP.NET MVC 3. However, several months ago, I started participating in Force.com development that isn’t declarative at all. Rather, it’s a full application running on top of Force.com and uses Visualforce and AngularJS.
I’ve learned quite a bit about the platform when it comes to custom applications while writing Apex code for backend processes. My team has adopted an interesting development workflow based on the differences of the platform in contrast to ASP.NET MVC or Node.js development. This post will describe our typical development workflow.
Development Workflow
It’s important to keep in mind that since the Force.com platform is very different from other platforms or technologies on the market, our development workflow is rather unique. Granted, it’s quite possible that many of you who are reading this post right now who partake in Force.com development may find your development workflow similar. I’d love to hear about your workflow in the comments.
Prerequisites
To follow this development workflow, the following prerequisites must be accounted for. Moreover, keep in mind, this guide is written strictly for Windows users.
Set Up Personal Developer Environment
Before getting started, we must have a Force.com cloud environment to work with, which will store our code, user settings, etc. This environment will be where we host our application. We can set up a free environment used for development and testing. In later stages of the development workflow, other cloud environments will be used that do not have the limitations of the free environment. However, for the moment, it should be more than sufficient for our development needs.
All that’s needed to get the free account is to visit developer.salesforce.com/signup. Fill out the form, and then you should be in business.
- Install Eclipse (Java version) and configure the Force.com IDE plugin.
- Install a good code editor like Notepad++.
- Have a source control system in place, such as Git.
- Install a good file comparison tool such as Beyond Compare, WinMerge, or KDiff3.
- Optional: Install a good search program such as Agent Ransack.
Set up a Folder (ideally in the root of C:) for Source Control
We require a place to store our metadata and all files to be part of our Force.com project. Creating a folder for this purpose is a must before continuing further. I recommend Git due to its powerful feature set and ease of use. If you aren’t into typing commands on the command prompt, then a great tool to use with Git is SourceTree.
Once you have a folder setup that is under source control, I suggest adding a .gitignore file to ignore items that aren’t recommended to be placed into source control. Below is an example of the file I use for this purpose.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
### Windows ### # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msm *.msp # Windows shortcuts *.lnk ### ForceDotCom ### .project .settings salesforce.schema Referenced Packages ### Directory for ForceDotCom exports ### export/ ### Apache Ant ### build.properties build.xml # Ignore personal property files # my.build.properties my.build.xml poc.build.properties poc.build.xml # Ignore static resource containers (.resource files) *.resource |
Configure Apache Ant and Account for Metadata
Apache Ant is a deployment tool used with the Force.com platform. It is used to capture data from a Force.com environment and to deploy data back to that environment. Its settings must be configured to work with Force.com metadata. Everything in Force.com has metadata. It could almost be said that this data is the heart of the platform. It tells Force.com how to behave, and properly capturing it and deploying it is key before starting any kind of development. I propose reading up more on metadata before continuing if you don’t have a firm grasp on it.
Apache Ant has three files that need to be configured to work with your instance of Force.com. However, before configuring those files, you need to ensure Apache Ant is installed and can work from the command line. Follow this guide before continuing. In addition, be sure to download the Force.com Migration Tool. This can be done by logging into your instance of Salesforce and going to Setup. Once there, search for tools and then download it. There will be a file named ant-salesforce.jar that needs to be copied to your Apache Ant lib folder.
Create the files described below and ensure they are placed into the root of the folder that is now under source control along with your .gitignore file.
build.properties—This file stores some important settings for Apache Ant to retrieve and make use of. Force.com usernames, passwords, instance URLs, and the local source path for where the metadata is stored can be found here.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# build.properties # # Specify the login credentials for the desired Salesforce organization developer.username = john.smith@greatcompany.com.devenv developer.password = iShouldChooseAGoodPassword #sf.pkgName = <Insert comma separated package names to be retrieved> #sf.zipFile = <Insert path of the zipfile to be retrieved> #sf.metadataType = <Insert metadata type name for which listMetadata or bulkRetrieve operations are to be performed> # Use 'https://www.salesforce.com' for production or developer edition (the default if not specified). # Use 'https://test.salesforce.com for sandbox. developer.serverurl = https://login.salesforce.com developer.local.source.path = C:\\Force.com_Projects\\Project_Amazing\\src |
build.xml—This file contains all the instructions for Apache Ant to carry out. Such instructions include running test classes as part of a deployment, converting line endings to Unix format, automatically adding changed static resources to the appropriate resource file, and what metadata to work with based on the XML file referenced. Usually package.xml.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
<project name="Example Build.xml for Ant" default="test" basedir="." xmlns:sf="antlib:com.salesforce"> <property file="build.properties"/> <property environment="env"/> <tstamp/> <!-- <property name="mydir" value='dev3-${DSTAMP}'/> --> <!-- <property name="mydir" value="code"/> --> <property name="mydate" value='${DSTAMP}'/> <!-- Remove Windows line endings and replace with Unix line endings --> <fixcrlf srcdir="${developer.local.source.path}" includes="**/applications/*.app **/classes/*.cls **/classes/*.xml **/components/*.component **/components/*.xml **/email/*.xml **/email/Project_Amazing_Email_Templates/*.email **/email/Project_Amazing_Email_Templates/*.xml **/labels/*.labels **/layouts/*.layout **/objects/*.object **/pages/*.page **/pages/*.xml **/permissionsets/*.permissionset **/portals/*.portal **/staticresources/*.xml **/staticresources/AllResource/css/*.css **/staticresources/AllResource/js/*.js **/staticresources/AllResource/lib/bootstrap/css/*.css **/staticresources/AllResource/lib/bootstrap/css/*.map **/staticresources/AllResource/lib/bootstrap/js/*.js **/staticresources/ProjectAmazingWebApp_Resources/css/*.css **/staticresources/ProjectAmazingWebApp_Resources/js/*.js **/staticresources/ProjectAmazingWebApp_Resources/js/*.map **/staticresources/ProjectAmazingWebApp_Resources/typescript/*.json **/staticresources/ProjectAmazingWebApp_Resources/typescript/Models/*.js **/staticresources/ProjectAmazingWebApp_Resources/typescript/Models/*.map **/staticresources/ProjectAmazingWebApp_Resources/typescript/Models/*.ts **/staticresources/ProjectAmazingWebApp_Resources/lib/bootstrap/css/*.css **/staticresources/ProjectAmazingWebApp_Resources/lib/bootstrap/css/*.map **/staticresources/ProjectAmazingWebApp_Resources/lib/bootstrap/js/*.js **/staticresources/ProjectAmazingWebApp_Resources/pages/*.html **/staticresources/JqueryDatatable/media/css/*.css **/staticresources/JqueryDatatable/media/js/*.js **/staticresources/PageResources/css/*.css **/staticresources/PageResources/js/*.js **/tabs/*.tab **/triggers/*.trigger **/triggers/*.xml **/workflows/*.workflow **/*.xml" eol="lf" eof="remove" /> <!-- dumps --> <target name="deployDev" depends="zip, deployCode" /> <target name="getCode"> <sf:retrieve username="${developer.username}" password="${developer.password}" serverurl="${developer.serverurl}" retrieveTarget="${developer.local.source.path}" unpackaged="package.xml" pollWaitMillis="10000" maxPoll="20"/> </target> <!-- deploys --> <target name="deployCode"> <sf:deploy username="${developer.username}" password="${developer.password}" serverurl="${developer.serverurl}" deployroot="${developer.local.source.path}" testLevel="RunSpecifiedTests"> <runTest>PA_UserServiceTest</runTest> <runTest>PA_ProfileServiceTest</runTest> <runTest>PA_AlphaTest</runTest> <runTest>PA_BetaTest</runTest> <runTest>PA_GammaTest</runTest> <!-- Additional test classes go here --> </sf:deploy> </target> <target name="deployCodeCheckOnly"> <sf:deploy username="${developer.username}" password="${developer.password}" serverurl="${developer.serverurl}" deployroot="${developer.local.source.path}" checkOnly="true" testLevel="RunSpecifiedTests"> <runTest>PA_UserServiceTest</runTest> <runTest>PA_ProfileServiceTest</runTest> <runTest>PA_AlphaTest</runTest> <runTest>PA_BetaTest</runTest> <runTest>PA_GammaTest</runTest> <!-- Additional test classes go here --> </sf:deploy> </target> <!-- zip resources --> <target name="zip" depends="zipAllResource, zipProjectAmazingWebApp, zipJqueryDatatable, zipPageResources"/> <target name="zipAllResource"> <zip destfile="src/staticresources/AllResource.resource" basedir="src/staticresources/AllResource" /> </target> <target name="zipProjectAmazingWebApp"> <zip destfile="src/staticresources/ProjectAmazingWebApp_Resources.resource" basedir="src/staticresources/ProjectAmazingWebApp_Resources" /> </target> <target name="zipJqueryDatatable"> <zip destfile="src/staticresources/JqueryDatatable.resource" basedir="src/staticresources/JqueryDatatable" /> </target> <target name="zipPageResources"> <zip destfile="src/staticresources/PageResources.resource" basedir="src/staticresources/PageResources" /> </target> </project> |
package.xml—This file is rather significant because it contains the metadata that Apache Ant uses to retrieve or deploy the appropriate data. All custom object definitions are contained within this file as well as workflow rules, email template definitions, etc. Basically, anything you’ve customized on the platform that you want to capture and place under source control must be contained within this file. If it’s not in the file, then if you did a deployment, whatever you missed would be skipped. The same thing goes when extracting the data.
Creating the package.xml can be done in several ways. You can manually create it, but it’s important to read up on the different types of metadata definitions you’ll need to include in your file. This all depends on what you wish to capture from the platform. Here’s a sample to get an idea. Moreover, here’s an article describing how to construct it.
Something else that can be done to get a better idea of the package.xml file is to take a look at the one generated by the Force.com IDE if you decide to use that tool. You can also find various videos on this subject as well.
Finally, it is significant that you prefix all of your objects in Force.com. Since this is an environment that can be shared among multiple applications, naming collisions are likely to occur. Everything should be prefixed with something that describes your application, like PA_, short for Project Amazing. Thus, if you had a class named ProfileService, then its final name would be PA_ProfileService. What you are mainly focused on is the API name. Keep in mind we cannot do this with standard objects, as the name is already given for us, but anything we create, dubbed custom objects, can have the prefix.
Other items in Force.com should have prefixes as well, such as triggers. The bottom line is to prefix anything you can in contexts where a naming collision could happen. If you do this, you’ll save yourself a lot of time and headaches when doing a release in an environment where other applications exist.
Workflow Process
- Pull Latest Code and Deploy to Personal Developer Environment—After getting the appropriate code for your project under source control, pull the latest code from the repository. Using Ant, deploy the code to your own personal Force.com Developer Environment.

Every time a developer commits code to the repository, it will be important to pull that code and deploy it to your personal developer environment using Ant. This ensures everything is up-to-date and accounted for. Keep in mind you’ll have to properly put the code under source control, and by that I mean choose what should be under source control and what shouldn’t (this was discussed previously). Moreover, getting your own personal environment initially configured to support your project requirements is a must. It’s best to have pre-deployment and post-deployment instructions recorded, as they’ll be helpful for any future releases.
Text Editor (ex: Notepad++) — If you are using a text editor like Notepad++ to work on code, you’ll likely be editing metadata locally on your hard drive. This includes Visualforce pages, Apex classes, triggers, etc. Anytime a file is edited that way, you’ll need to use Apache Ant to deploy those changes back to your personal developer environment.
Force.com IDE (Eclipse) — If you are editing or creating any code in the Force.com IDE, anytime you save something it will immediately get synced back to the server. However, this is more complicated as you’ll likely have a separate folder that has the project and files for use with the IDE and then another separate folder under source control. So how do you get your changes back in sync? Using a tool like Beyond Compare, you can compare the two files (the one you changed in the IDE with the one in source control) and then merge the changes. That way, you can account for those changes in source control. You may not have to use Apache Ant to deploy back to the server depending on what file you modified, since the IDE will have already pushed your changes to the server.
Tip: Always run your test classes regularly. Waiting until the end to run test classes before a release is bad practice. Depending on how long you go without updating your tests, a large body of work could be waiting for you right before you release, and this could have a very negative impact on your schedule. Staying on top of it every day may require a bit of extra time, but it pays off huge eventually. Plan for extra time to tackle these tests.
Important Note: Out of the box, Force.com doesn’t have regression testing or more advanced automated testing features. Any developer knows how nasty regressions can be; they aren’t any fun. Why not find a solution to nip those in the bud now? There are solutions out in the wild that are worth investigating.
Static Resources: Force.com handles static resources very differently from other platforms. Static resources in this context are images, CSS & JavaScript files, etc. There is a limit to the size of each static resource and the size of all of them combined. When editing a static resource file under source control, you’ll generally find it under the folder named staticresources. You’ll also find other files with a .resource extension. This is an archive file that contains all the static resources for that particular context. Just editing the static resource file alone and then doing a deployment is not enough. In order for your change to be picked up by the platform, the updated file needs to be in its parent .resource file. Force.com uses the resource file to retrieve your static resources.
There is good news, however. The example build.xml file shown previously automatically adds any changed static resources back to the resource file. You no longer need to worry about adding it yourself. This happens when you use Apache Ant to do the deployment. Resources do not work the same way in the Force.com IDE, and I’ve always found it easier to use Apache Ant to deploy updated static resources.
Conclusion
I’ve just gone over the Force.com Development Workflow that I (along with my team) currently use. I’ve found this workflow to be the best for our needs. You can certainly do things very differently from how I’ve described, and that is entirely up to you. It’s important to pick a workflow that works best for your given circumstances. Not every workflow will be appropriate to your situation. What I’ve shared here offers valuable ideas to customize your workflow.
It’s essential to keep in mind that the Force.com platform is constantly evolving. This example workflow may not be applicable in the future. I haven’t covered MavensMate, which is certainly worth exploring (thanks go to my cohort for introducing this to me). Other tools will certainly be introduced in the future. I also haven’t discussed debugging on the platform, which is an entirely different topic unto itself.
Please share your experiences with your development workflow in the comments below. I look forward to reading them and banding together as a community to identify different methods of getting work done on the platform. Force.com is certainly very different from other so-called traditional software development methods I’ve used in the past and takes time to get used to. However, when you discover its power and unlock it, then you have a valuable set of tools available to achieve the goals that are important to you. That’s why identifying methods to work with it effectively is critical to everyone’s success.
As with any project, it is essential to identify the correct technology to solve your business problems. With that being said, always weigh your options before committing to a particular technology/framework/etc. Know the pros and cons of each one and do your research. Happy Salesforcing.
