3 days here and I’m sad to be leaving Istanbul. The morning fog over the harbor is slowly burned away by the sun, as fishing boats, ferries, and the occasional yacht dart from one waterway to the next. To the right of me, the Hagia Sofia rises up over the city, its spires punctuating the morning skyline.
My room is a patchwork of faded brick, handcut and worn-down hardwood floors, leather chairs and bare stained concrete walls. The concrete itself is sanded smooth, but with the random discoloration of dozens (if not hundreds) of years of paint upon paint layers now gone.
A light spring breeze teases through the open windows, draperies pushed to the side but still free to twist and shimmer in the sun. The floors squeak with every movement; I wonder if the neighbors downstairs are bothered, but yet I’ve heard nothing from my upstairs companions. Seagulls and pigeons careen past, skimming the roofs and - ever so often - breaking out into a brawl over some perceived injustice.
The tugboats and ferries bray in the harbor, and in the evenings when the cruise ships set sail back to the open ocean, the entire bay rebounds with their long, low departure call.
Not to be outdone, in the evenings - and mornings, and several times during the day - the call to prayer covers the city. Different men, each with the same song but unique voices, call out to their brothers and sisters over loudspeakers, bullhorns, and sometimes simply as a sole resonant singer, walking through the cobblestone streets of Istanbul.
It’s a beautiful, timeless city. I will miss it.
[This is a copy of a recommendation email I sent as Chief Scientist to my firm. I’ve reposted it here as an open letter because I feel it has a broad applicability as well]
I’ve been playing around with some new technologies lately, and I think I’ve reached the point where I’m comfortable enough to make a few recommendations. I really really like the four layers below in combination. It works well with our skillsets and process, it solves some hiring issues, it has good redundancy/scalability, and it cleanly separates areas of responsibility.
1. Back-end Tier: Salesforce.com/Database.com (DBDC). Nuf said. All of our development process, configuration, apex code, triggers, workflows, emails, etc all work as normal.
2. Deployment/Connector Tier: Heroku using the Database.com connector. Also nuf said. Once you get over the hurdle of getting the thing setup and running in Windows (OS X and Ubuntu versions are easier), it’s a fantastic development platform. It offers a quick and easy deployment, is 100% revision controlled, has a great management UI, is not that expensive (free for low usage), and is language independent (Ruby recommended but not required; Enterprise Java can be run just as easily using the normal SOAP/REST APIs).
https://github.com/heroku/databasedotcom (scroll down for the readme)
http://rubydoc.info/github/heroku/databasedotcom/master/frames (API documentation)
3. Middleware/Application Tier: Ruby + Sinatra. This is NOT Ruby+Rails (RoR), just so we are clear. RoR is big & heavy, and contains a lot of extra framework stuff that is going to simply get in the way since we will both custom views side stuff (the UI) as well as Database.com on the back-end (the model). That leaves the controller tier useful only, and that’s just not clean enough in RoR.
Sinatra, on the other hand, is simplicity, beauty and power:
http://www.sinatrarb.com/intro
The way I’ve decided I like to use it best is as a DBDC -> JSON app server, where the app sits hosted on Heroku (with all the scaling benefits there), and it simply takes in get/put/post requests by URL, communicates with DBDC to insert/update/delete/query to unerlying data, and then take the response and return a formatted JSON string. Here is a real world example I’ve been playing with (this is 80% of a complete working endpoint; only the DBDC client login steps were left out):
get "/data/user/:id" do
content_type :"application/json"
# Talk to Database.com and fetch the user record by ID
user = User.find(params[:id])
# Build and return the JSON in a single step
# (abstracting the API names in the process)
response = {
"user_id" => user.Id,
"user_name" => user.Name,
# Related list data "tasks" => [],
"meetings" => []
}.to_json
end
Because of the way our DBDC instance is handling auth, the model, triggers, etc: the middleware layer actualy has to do very little logic and work. It’s essentially fetching the data, sorting and ordering it based on some generic filters, and then passing the JSON back to the client. Users can’t see/update/delete things this don’t have access to, but it’s NOT Sinatra that enforces that, it’s DBDC, so we don’t have to put all this extra code/logic in the controller tier. Very clean.
OAuth is used by the client to login to the SF backend. Heroku does no auth themselves, only passes session tokens around.
Also, here are the steps to deploy a new Heroku version (development or production):
$ git add . $ git commit -m "Updated release" $ git push heroku master
And you are done!
4. Front-End Tier: Pretty much anything then that understands how to retrieve and process JSON, which these days is basically everything. This means that any open-market developer that knows how to build a web application, execute Ajax REST requests, and process the data is now available for hire. We can find people that know how to build great UIs (onshore or offshore), without having to restrict ourselves to the niche market SF skillset, because our approach completely black-boxes the DBDC tier behind the JSON tier, and removes any ability for the web guy to even care about the SF/DBDC implementation.
The front-end UI can then either (a) be deployed with the Sinatra app as HTML+CSS+Jquery or (b) can be run as a completely separate tier on Amazon EC2 (for example). Uploaded data (such as images, videos, documents, attachments, and such) can also be pushed straight to Amazon S3 using one of the easy-to-install addons, and then the URL to that resource is simply stored in SF/DBDC.
This second approach would gives you three separate scalability tiers: Database.com itself, Heroku+Sinatra, and Amazon EC2. Each tier can be scaled/costed as needed to support the usage model of that particular tier. For example: you can have a single SF.com license that ALL admin usage/network communication occurs under, talking to a scaled-up Heroku tier (because the controller processing is where most of the heavy lifting is happening), talking to a very light EC2 tier (because all it’s doing is really serving static HTML+CSS at that point.
Open Source Code + QuadCopters+ QT = HUGE Win! Really, I’m very very excited. :)
Robot Quadrotors Perform James Bond Theme (by UnivPennsylvania)
This is so amazingly geeky. :)
Quick Tip.. Instead of doing this:
<apex:sectionHeader title="Opportunities" />
Do this instead:
<apex:sectionHeader title="{!$ObjectType.Opportunity.LabelPlural}" />
Using $ObjectType allows you to quickly and easily inject metadata APIs into Visualforce Pages. Other examples are:
$ObjectType.Account.Label => "Account"
$ObjectType.Account.LabelPlural => "Accounts"
$ObjectType.Account.Fields.BillingAddress.Label => "Billing Address"
This works flawlessly with renamed object labels as well. Even better, most of the SObject Describe() methods (or at least the ones that start with get) work also:
<apex:outputLink value="/{!$ObjectType.Account.KeyPrefix}/e">New</apex:outputLink>
Of course, using the URLFOR() function makes more sense in that context:
<apex:outputLink value="{!URLFOR($Action.Account.New)}">New</apex:outputLink>Here is how you create a dropdown menu button similar to the the “Clone Products” menu button on a standard Salesforce Opportunity page layout.
Step 1: Use a Visualforce Page
This code only works from the context of a custom Visualforce Page, so that is where you have to start.
Step 2: Place your tags
Place a mixture of standard Apex tags (<apex:outputPanel />) and standard HTML tags (<div />) with special HTML IDs defined. We have to do it this way unfortunately, as opposed to just using standard Apex tags, because of the way Salesforce’s javascript menu button code works. More details below.
The tag structure is very straightforward, and looks like this in your Visualforce Page:
<apex:outputPanel style="padding-left: 1em;">
<div class="menuButton" id="Actions"><apex:outputPanel layout="none">
<div class="menuButtonButton" id="ActionsButton"><span class="menuButtonLabel" id="ActionsLabel">Actions</span></div>
<div class="menuButtonMenu" id="ActionsMenu"><apex:outputPanel layout="none">
<apex:outputLink value="#">Menu Link #1</apex:outputLink>
<apex:outputLink value="#">Menu Link #2</apex:outputLink>
<apex:outputLink value="#">Menu Link #3</apex:outputLink>
</apex:outputPanel></div>
</apex:outputPanel></div>
</apex:outputPanel>
<script type="text/javascript">new MenuButton('Actions', false);</script>
You’ll also notice above that I’d bolded a few specific areas. These are the places that all have to match if you decide to change the “Actions” term to something else.
Step 4: Replace each # with your actual link.
You can do this any number of ways:
Step 5: Deploy and profit!
We hope… :)
Roll-Up fields are conceptually nice, but in practice are a huge pain to work with. Here are the quick benefits:
But after you’ve used them for a while, they start to cause problems:
My solution to date (and the one I’ve found that works the best) is to simply have a single roll-up field per related master-detail object, and for that one roll-up to define it as MAX(LastModifiedDate). The net effect of this is that anytime the related object changes in any way (insert, update, or delete), an update call is cascaded to the parent level.
Then, all the actual aggregation fields that you need are created as plain Jane number/datetime/currency fields, and a simple “before update” trigger runs on the parent to manually (and selectively) aggregate child information into the parent.
Some caveats:
But I personally feel that for a lot of use cases (and I very strongly suggest this approach in an enterprise environment) this approach is architecturally more sound:
Regarding bullet point #3 there: Try creating three custom fields, named something like this:
The first is the auto-calculated value that is aggregated in the roll-up trigger; the second is the optional modified value that a user might fill in to override the auto calculation; and the third is a formula field that returns the modified value (if set) or the calculated value (by default).
Rinse/repeat this pattern for all of the aggregated field names that your particular business process needs.
As part of my day job as Chief Scientist at roundCorner (versus my evening job moonlighting as a travel agent and traveling to odd places), I frequently have to review -and cringe at- a lot of the code written by outsourced developers.
Now, I think we can all agree that outsourced devs are generally not the best when it comes to good coding, but in the interest of education, I’d like to take a minute and give a quick down & dirty tip. Here’s an example of something I see a lot: the 0-index-only trigger:
trigger changeSomethingImportant on Account(before insert, before update) {
var acc = trigger.new[0];
if (acc.Some_Custom_Picklist__c == 'True Stuff')
{
acc.Other_Flag__c = true;
}
else
{
acc.Other_Flag__c = false;
}
}
Now, I don’t ever write my code this way. There are all sorts of problems, but here is the biggest one: the trigger.new[0] statement. Apex Triggers as designed by Salesforce are meant to handle BATCHES of records at once, up to 200 in a single pass. What the trigger above is doing is looking at only the very first record (in a potential list of 1 to 200 records) and checking the value for that one only. The net effect of this is that when your clients/business analysts do their User Acceptance Testing (UAT) in their web browser on an Account by Account basis, the trigger works fine, and every one is happy. It isn’t until either (a) some other code tries to bulk update accounts, or (b) some poor support admin tries to data load 50,000 accounts does the code suddenly (and silently) fail to work.
For brevity’s sake, here is what that code should look like, simplified and formatted:
trigger changeSomethingImportant on Account(before insert, before update) {
for(Account account : trigger.new) {
account.Other_Flag__c = 'True Stuff'.equalsIgnoreCase(account.Some_Custom_Picklist__c);
}
}
You’ll notice that in addition to now looping over all 1-200 potential records at once, I’ve also gotten rid of the if-else statement (by doing a direct Boolean assignment), as well as using a special String method called “equalsIgnoreCase”, which does the picklist value comparison in a case-insensitive manner.
If you have technical questions, you can always head over to my Ask Ian Anything page. I’ll try to answer as best as I can.
Sitting in a log cabin in upstate NY when I was 3 years old. What is your earliest non-human memory?