CacheService vs Script Properties vs a Config sheet — pick the right shelf

TL;DR — A practical decision guide: CacheService for short-lived TTL values, Script Properties for small secrets and flags, a Config sheet for human-editable tables — with a small decision table and anti-patterns. Plus a soft NitroGAS note on annotating why each value lives where it does.

Every Apps Script project eventually grows “settings.” The trouble isn’t storing them — it’s storing them on the wrong shelf. API tokens end up in a visible tab. A 50-row client lookup ends up JSON-stuffed into one property. Exchange rates get cached for a week and nobody knows why totals are stale.

Three shelves cover almost everything:

Shelf Best for Lifetime Who edits
CacheService Expensive lookups, API responses, “good for 10 minutes” Minutes → up to 6 hours (TTL) Script only
Script / Document Properties Small secrets, ids, feature flags, environment switches Until you change it You (developer)
Config sheet Tables humans edit: client → folder, thresholds, email lists Until someone edits the sheet Ops / non-devs

TL;DR

  • Cache anything you can safely recompute; set a TTL; never store the only copy of truth there
  • Properties for small secrets and flags — tokens, sheet ids, dryRun=true. We already went deep on this in Script Properties for bootstrapper config — use that for the how-to; this guide is about choosing
  • Config sheet for tables people should change without opening the script editor
  • Anti-pattern alarm: secrets on a sheet, huge tables in properties, business data in cache
  • Soft CTA: ask Co-Pilot to annotate why each value lives where it does

Using the Apps Script editor a lot? NitroGAS drops free themes & snippets right into script.google.com — optional Co-Pilot when you want a boost.

CacheService — short-lived and disposable

Cache is a speed bump remover. Use it when:

  • The value is expensive to get (UrlFetch, big getValues, slow API)
  • It’s safe to recompute if the cache is empty or evicted
  • A little staleness is OK (you pick the TTL)
function getRatesCached_() { var cache = CacheService.getScriptCache(); var hit = cache.get('fxRates'); if (hit) return JSON.parse(hit); var rates = fetchRatesFromApi_(); // expensive cache.put('fxRates', JSON.stringify(rates), 600); // 10 minutes return rates; }

Not for: secrets you can’t regenerate, “the” customer list, anything you’d be sad to lose. Cache can be evicted early; entries have size limits (keep values small; about 100KB per entry is the ceiling).

Script / Document Properties — small, private-ish, stable

Properties are where environment knobs live:

  • API tokens and webhook URLs (see the Script Properties guide for helpers)
  • Target spreadsheet / folder ids
  • Feature flags: enableEmails, dryRun
  • Per-document settings (Document Properties) for bound scripts cloned per client

Not for: big tables, frequently changing counters (write limits), anything ops should edit without you.

Config sheet — tables humans own

A Config (or Settings) tab is ideal when:

  • Non-developers need to change values (thresholds, notify emails, client → folder map)
  • The data is tabular (many rows, several columns)
  • You want visibility: filters, comments, “who changed this?” via sheet history
function readConfigTable_() { var rows = SpreadsheetApp.getActive().getSheetByName('Config').getDataRange().getValues(); var headers = rows.shift(); return rows.map(function (r) { var o = {}; headers.forEach(function (h, i) { o[h] = r[i]; }); return o; }); }

Cache the parsed table if you read it on every edit — then you’re using two shelves on purpose: sheet = source of truth, cache = speed.

Not for: secrets. Anyone with edit access can see and copy them.

Decision table (pin this)

Question Shelf
Will a teammate edit it without the script editor? Config sheet
Is it a secret or an id you’d rotate? Properties
Is it expensive to compute and OK if it expires? Cache
Is it a 200-row lookup table? Config sheet (+ optional cache)
Is it dryRun / stagingMode? Properties
Is it an API response you hit 50 times per run? Cache

Anti-patterns we keep seeing

  1. Token in Config!B2 — visible, copyable, travels with every copy of the file. Move to Properties.
  2. JSON megablob in one property — hard to diff, easy to clobber. Split keys, or move tables to a sheet.
  3. Cache as a database — “it’s been in cache for weeks” until it isn’t. Cache is not durable storage.
  4. Human-editable table in Properties — ops can’t see or change it; you become the bottleneck.
  5. No TTL thinking — caching exchange rates for 6 hours on a pricing sheet that updates hourly.

Soft Co-Pilot note

NitroGAS keeps cache and properties helpers a search away in the editor. Paste your config block and ask Co-Pilot to annotate why each value lives where it does — “cache: recomputable FX”, “property: webhook secret”, “sheet: ops-owned thresholds.” Future you (and the next contractor) will thank you. Free core; Co-Pilot is a pass for when you want the annotations fast.

Closing checklist

  • No secrets on a visible sheet
  • Every cached value has a TTL and a recompute path
  • Human-edited tables live on a Config sheet
  • Properties hold small ids / flags / tokens only
  • A short comment next to each value says which shelf and why

Happy Coding!