Salesforce applications can sometimes be difficult to debug. The debug
log often has the information you need, but finding it isn't always easy.
For example, in a recent project, I needed to figure out why performing an
action from a Visualforce page was causing more SOQL queries than expected,
generating warning emails as we neared the governor limit. This was a large
application that had been under development for about eight years; many different
developers had been involved in various phases of its development.
As with many Salesforce applications that have been through many phases of
development, it wasn't obvious how multiple triggers, workflow rules, and
Process Builder flows were interrelated.
It was easy enough to figure out which SOQL queries were running multiple times
by grepping the debug log, but it wasn't obvious why the method containing the
query was being called repeatedly.
In researching ways to analyze call paths, I found Brendan Gregg's work on flame graphs, which
visualizes call stacks with stacked bar charts. (You might also be familiar
with Chrome's flame
Visualizing the call path through stacked bar charts turned out to be a good
way to make sense of the Salesforce debug log.
So I created Apex Flame
to visualize Salesforce debug logs using flame charts or flame graphs.
You can drill in by clicking on a statement, or click on Search to highlight
statements that match a string.
I'd love to get some feedback from other Salesforce developers. Try out Apex Flame, and
let me know what you think.
As for the debugging issue that initially inspired the creation of Apex Flame, it
turned out to be the combination of a trigger that wasn't checking whether the
old and new values of a field were different, causing it to run code
unnecessarily, combined with a workflow rule that was causing the trigger to be
called multiple times.
Another recent case in which it turned out to be helpful was in troubleshooting
a process builder flow that was failing to update child records as expected.
The flow assumed that if the parent record is new (ISNEW()) that
it didn't have child records. It turns out this isn't necessarily true because
a trigger on the parent object can create child records, and the flow runs
after the after insert trigger. The order
of execution is documented, but visualizing the execution order made it
clear what was causing the unexpected behavior.
Based on my previous Force.com app for managing Salesforce
Cases on a Kanban board, I've built Kanban for Salesforce, a project
management tool on the Force.com platform. It uses a custom object for cards
so it's not tied to an existing Salesforce native object. Cards can be
organizated into sprints.
I've started building a tool to manage Salesforce Cases on a kanban board.
Each case is a card, and each Status picklist value is a list on which the
cases can be organized. You can drag cases around to prioritize and track
The lists are organized left-to-right in the same order that the picklist
values are arranged top-to-bottom. There's a custom setting to treat the first
picklist value, typically "New", as your backlog. The backlog is hidden by
default, and can be shown by clicking the arrow on the top left.
If you use Salesforce Cases, please try it out. I would love to get some
feedback and suggestions for features. It's still early in development, but I
was able to get off to a good start on the front-end by adapting code from huboard, a great tool for
managing github tickets.
Static Resources in Salesforce are often zip files containing multiple files.
If you're keeping static resource under version control using git, here's how
to get useful diffs for them whether they are zip files or single text files.
Create a shell script which will identify whether a file is a zip file or not.
If so, it should unzip the contents to stdout; otherwise, it should just output
the contents of the file. I called it resource-conv.
Configuring Salesforce Chatter
with an account in an org that doesn't have Chatter enabled results in an
unusable app. Upon start-up, it authenticates against the org, shows an error,
"Chatter is not enabled for this organization", then exits.
To restore Chatter to a state where it prompts for the org type, Default or
Sandbox, I had to remove the Encrypted Local Store
used by AIR apps.
Saleforce uses 15-character IDs as primary keys for every record. When
retrieving an ID over the API,
18-character case-insensitive IDs are returned. Generally, this doesn't cause
any problems because the 18-character version can be used anywhere the
15-character one is. One problem arises though when you are storing IDs in a
text field. In this case, you may need to convert a 15-character ID into its
18-character version. Salesforce provides a description of how
to perform this conversion.