google-apps-script
Google Apps Script
Server-side JavaScript that runs on Google's infrastructure with built-in, auto-authorised services for Workspace. V8 is the only runtime: Rhino was shut down on 31 January 2026, so write modern JavaScript (const/let, arrow functions, classes, template literals, destructuring) and set "runtimeVersion": "V8" in appsscript.json if a legacy manifest still says DEPRECATED_ES5.
Steps
-
Pick the binding and entry point. Container-bound scripts (opened from a Sheet, Doc or Form) can use
getActiveSpreadsheet()/getActiveDocument()and simple triggers (onOpen,onEdit). Standalone scripts and anything run from a time trigger must open files by ID or URL. Done when every file access in the plan uses a call that works in the chosen binding. -
Write against the service, batching I/O. Read a whole range with
getValues(), transform in memory, write back once withsetValues(). Cache expensive lookups with CacheService and persist config or cursors with PropertiesService. Done when nogetValue/setValue/appendRowsits inside a loop over rows. -
Fit the 6-minute limit. Each execution stops at 6 minutes (custom functions at 30 seconds). For larger jobs, process a slice, save a cursor in PropertiesService, and let a time trigger pick up the next slice. Done when the worst-case run is bounded by a batch size, not the data size.
-
Set triggers idempotently. Before
ScriptApp.newTrigger(...), delete existing triggers for the same handler so re-running setup never duplicates them. Done when running the setup function twice leaves one trigger per handler. -
Narrow the scopes. List
oauthScopesexplicitly inappsscript.json, preferringspreadsheets.currentonly,drive.fileandscript.send_mailover their broad counterparts. Done when every scope in the manifest maps to a call the script makes. -
Handle failure visibly. Wrap trigger handlers in try/catch, log with
console.error(it reaches Cloud Logging with severity), and notify by email or an error sheet - a failed time trigger is otherwise silent. Done when every trigger handler has a catch that records the error somewhere a human will see it.