持続可能なGASトリガー管理のベストプラクティス
こんにちは、luthです。
GAS(Google Apps Script)は、生成AIでもパっとコードを出力してくれ、手軽に業務効率化ができる素晴らしいツールです。
しかし、その手軽さゆえに、作ったツールが「個人の持ち物」のまま運用され、作成者の異動や退職とともに「負の遺産」になってしまうことが多々あります。
特に、「トリガー(自動実行)」の管理は、チーム開発において最もボトルネックになりやすいポイントです。
個人ツールや、一瞬しか利用しない短期ツールであれば問題になりませんが、業務やチームで利用するのであれば、数ヶ月・数年の単位で稼動する必要があります。
今回は、未来のチームメンバーが安心して引き継げる、「持続可能なトリガー管理」の設計パターンをご紹介します。
🐣 なぜ「トリガー」の管理設計が重要なのか
GASにおけるトリガー(時間主導型やイベント主導型の自動実行設定)には、チーム運用を考える上で避けて通れない仕様があります。
1. トリガーは「作成者のアカウント」と紐付いている
トリガーは、設定したユーザーのアカウントに紐付きます。
もし、設定者が退職してアカウントが削除されると、そのトリガーは「無効」状態となり、ある日突然、人知れず停止します。
「あれ?今朝のレポートが来ていない?」「なんかフォーム投稿されても、Slackに通知が来ない」と気づいた時には、重要な業務プロセスが既に止まってしまっています。
2. 「見えない・触れない」アクセス権の壁
逆に、アカウントは残っているものの、異動などで「ファイルの編集権限」だけが外れた場合、トリガーは動き続けますが、スクリプトがエラーを吐き続ける状態(いわゆるゾンビトリガー)になります。
さらに厄介なのが、**「他人のトリガーは、管理画面から削除・編集ができない」**という点です。
管理者は「誰かのトリガーが動いている」ことは分かっても、「具体的に誰のアカウントか」を特定したり、それを停止させたりすることができません。
3. 「二重稼働」のリスク
エラーを止める手段がないため、後任者がやむを得ず「自分のアカウントで新しいトリガー」を設定すると、「エラーを出し続ける前任者のトリガー」と「正常な後任者のトリガー」が重複して動くことになります。
💡 解決策:プログラムによる「セルフクリーニング」機能
管理画面から制御できないなら、コードの中に「自律的に停止する機能」を組み込んでおくのが、スマートな解決策です。
具体的には、ScriptProperties(スクリプトプロパティ) を「フラグ管理ストア」として活用します。
外部からフラグを立てることで、古いトリガーが自らその役目を終える(削除される)仕組みを作ります。
この設計を入れておくことで、前任者がいなくなってもスムーズに「後任者による運用設定の上書き」ができるようになります。
運用のイメージ図
🛠️ 実装パターン:チームのためのコード
このロジックは非常にシンプルです。
既存のスクリプトの冒頭に、以下のチェック処理を追加するだけです。
1. セルフクリーニング機能付きのメイン処理
関数の実行時に「停止命令が出ていないか」を確認し、出ている場合はそのまま自身のトリガーを削除します。
/**
* トリガー設定対象の関数
*/
function mainTask() {
// 1. 共有プロパティからステータスを確認
const props = PropertiesService.getScriptProperties();
const resetFlag = props.getProperty('TRIGGER_RESET_FLAG');
// 2. リセットフラグが立っていた場合、クリーンアップを実行
if (resetFlag === 'true') {
console.warn('リセット命令を検知しました。旧トリガーを削除し、運用を終了します。');
cleanUpTriggers('mainTask'); // 自身の関数名を指定
clearResetFlag(); // 後述のフラグ削除の関数。再設定可能な状態に移行
return; // 業務処理は行わずに終了
}
// === ここから通常の業務処理 ===
console.log('通常処理を開始します。');
// ...
}
/**
* 指定した関数のトリガーを削除するヘルパー関数。トリガーの起動条件に関わらず全て削除する。
* @param {string} functionName 削除したいトリガーが対象とする関数名
*/
function cleanUpTriggers(functionName) {
// getProjectTriggers() メソッドは
// 「実行者アカウントの設定しているトリガー」しか取得できないため
// 他ユーザー(後任者など)のトリガーは安全
const triggers = ScriptApp.getProjectTriggers();
triggers.forEach(trigger => {
// 自身の関数をハンドリングしているトリガーのみを対象にする
if (trigger.getHandlerFunction() === functionName) {
ScriptApp.deleteTrigger(trigger);
}
});
console.log(`関数 ${functionName} に紐づくトリガーの削除が完了しました。`);
}
2. リセット実行用関数(運用交代時のスイッチ)
引き継ぎを行う際、後任者はrequestTriggerReset()関数を一度だけ実行します。これにより、次回起動時に前任者のトリガーが自動的に削除されます。
/**
* トリガーのリセットフラグを立てる関数
* メニューやボタンなどに登録しておくと便利
*/
function requestTriggerReset() {
PropertiesService.getScriptProperties().setProperty('TRIGGER_RESET_FLAG', 'true');
console.log('トリガーのリセットフラグを設定しました。次回実行時に古いトリガーは自動削除されます。');
}
/**
* トリガーのリセットフラグを削除する関数
*/
function clearResetFlag() {
PropertiesService.getScriptProperties().deleteProperty('TRIGGER_RESET_FLAG');
console.log('リセットフラグを解除しました。新しい運用を開始できます。');
}
🚀 まとめ:コードに「思いやり」を実装しよう
GASでの開発は、動くものを作って終わりではありません。
**「そのツールが、自分がいなくなった後も健やかに動き続けられるか」**を考えることが、エンジニアとしての品質を高めます。
- アカウントに依存しない管理の仕組みを作る
- 「終わり方」をプログラムしておく
このひと手間を加えるだけで、あなたの作ったツールは「誰かの置き土産」ではなく、長く愛される「チームの資産」へと進化します。
ぜひ、次の開発からこのパターンを取り入れてみてください。
Discussion