Google Apps Script Development - Best Practices
This is an overview of the various techniques and best practices I have evolved in developing Google Apps Scripts over the years. Of course Google have got a few suggestions of their own, and there are plenty of more general JavaScript best practice guides out there, and even a few GAS ones – this one is pretty!
Get in touch now if you need any help with creating your own GAS scripts or have a look through some other free scripts and snippets.
Style
I try to keep to Google’s JavaScript style guide with a nod to Doug Crockford’s suggestions and always running it through JSHint to keep me in line! I use the simplified JSDoc GAS supports, and I won’t go into my spacing policy but I do make sure there is lots of it and try to stay consistent.
Another great resource is this nice summary of “Uncle Bob’s” Clean Code principles applied to JavaScript.
Logging/Debug Trace
I have created my own logging library – BBLog – based on the BetterLog library, which provides features like:
- Logging to a GSheet or Firebase DB
- Creating multiple logging instances
- Automatic logging of function names and line numbers
- Automatically log the user’s email address or ID, in a full or disguised format
- Optimising the Script
It’s mainly service calls that slow down your code (more on that here), so along with the debug trace you are already using take a look in View>Execution transcript in the script editor to see any service calls that are unnecessarily inside loops, etc.
Error Handling
The overriding principle here is to make a distinction between operational and programmer errors in the code; keep programmer errors specifics away from the user (hopefully these will all have been dealt with during development) and cleanly deal and back out from the operational errors – things caused by the outside world like user input mistakes, external resources not being available, etc. This article is more to do with developing JavaScript for Node but it’s a great article on error-handling generally.
So I make sure any calls from within event handlers (simple or installable triggers, things like a file opening, form submission, etc.) are inside a try/catch so all errors thrown can be tidily logged and re-thrown in development, and either handled or reported to the user in production.
Testing
I have tried out a few of the testing frameworks libraries available for GAS, but decided to roll my own around the underscoreGS library [link TBD]. I found QUnit for GAS too cumbersome and I’d never get around to updating it and GSUnit was replicating a lot of the functionality I’d already included with underscoreGS. So I’ve got a bit more pragmatic and create simple unit tests functions in each module if they need them and they aren’t going to be easy to test as a user; things like lower level utility functions. These individual module test functions are all called from a master test function which I’ll link to a debug menu in the sheet or doc. The rest of the testing is done manually. My scripts haven’t yet got complicated enough to justify a completely simulated GAS environment for fully-automated testing. Although you can always develop your standalone scripts locally in eclipse, and stub-out or create emulated Google Services – but I’ve not tried that yet (I’m sure I’d read about someone doing this but couldn’t find the link).
Debug Menus
I find a dedicated debug menu in the sheet or doc can be useful to avoid having to keep going into the script editor to run functions during development.
Design Patterns
I nest private functions to avoid too much global clutter:
function aFunction() {
var aVariable = localFunction()
function localFunction() {
// Do something that isn't accessible outside this function
}
}
and wrap all the functionality in each script file in it’s own namespace by simply wrapping it in an object:
var NamespaceObject = {
publicMethod: function() {
Logger.log('public method called');
},
publicProperty: 'some value',
};
NamespaceObject.publicMethod(); // public method called
Logger.log(NamespaceObject.publicProperty); // some value
or use the module pattern if I’m wanting to have functions private to that object:
var NamespaceObject = (function() {
var namespaceObject = {};
namespaceObject.name = 'NamespaceObject';
namespaceObject.showName = function() {
Logger.log('name: ' + namespaceObject.name)
};
return namespaceObject;
function privateFunction() {
Logger.log('Some private stuff');
}
})()
NamespaceObject.showName(); // name: NamespaceObject
Logger.log(NamespaceObject.privateFunction()); // TypeError
Libraries
There are a whole suite of different GAS libraries available for different purposes, I list the main ones I use elsewhere, and there is a fairly comprehensive list available here.
Exponential Backup
The Google services can occasionally fail for no apparent reason, so one interesting snippet worth mentioning is for using exponential backup to make service calls, i.e. re-trying a call at different intervals if it initially fails.
Google APIs
There are limits to the Google App functionality that is exposed via the built-in GAS Services, however more can be accessed via the Google APIs they provide for all types of web apps to use. Here’s the Drive API for example. These are a bit trickier to use as you are poking around within JSON responses, and having to carry out authorisation but the extra power can be worth it some times – and they usually operate faster.
Bruce McPherson has created a useful library for using the Drive SDK.
Version Control/Configuration Management
In the past version control with Apps Script has meant either taking lots of copies of your script projects, or setting up the fantastic but complicated GasGit. What really revolutionised the ease with with you can do “proper” version control is the “Google Apps Script GitHub Assistant“. This is a Chrome extension that allows you to “push” and “pull” to specific branches in a GitHub or BitBucket repo and create dev flows like this.
You can store versions of your script using File>Manage versions… in the script editor, however these old versions can be awkward to get to.
You can just develop locally and use this Node utility or Clasp to sync your files to Github and the load into the online script editor for running, but things will get messy if you start making changes in the script editor as well!
Here is a GDoc describing a dev work flow using GitHub.
GAS Framework
To get me up and running, and observing some of the above off the shelf I start most of my scripts with GASFramework. This forces a single entry point for public methods, and consistent logging and error handling.
Or take a look at Amit Agarwal‘s App Script Starter – a “modern software development environment for building Google add-ons and web applications with Google Apps Script and next-generation JavaScript“.
Further Reading
An article from Lucid – “The 6 deadly sins of Google Apps Script Add-on development”