Build a 360-degree feedback collector
Gather and aggregate multi-rater reviews for Northwind teammates anonymously.
Published Aug 14, 2025
360-degree reviews only work if people answer honestly, and people only answer honestly if they trust the process is anonymous. Northwind wants every teammate’s manager, peers, and reports to weigh in — but the moment a reviewer suspects their name is attached to a comment, the feedback turns bland and useless.
This automation keeps the raw answers and the running totals apart. A shared
Form collects the reviews; the raw responses land on a restricted sheet that
only HR can open. Meanwhile this trigger maintains a separate Aggregates
sheet that holds counts only — never the comments — so the people being
reviewed can see how many responses they have without ever seeing who said
what.
What you’ll need
- A shared Google Form with at least three questions, titled exactly:
Who are you reviewing,Strengths, andAreas to improve. - The Form’s linked responses sheet — keep this one restricted to HR. The raw, attributable answers live here.
- A separate spreadsheet for the rollup, with a first tab carrying the headers
subject,strengthsCount,improveCount. This is the safe-to-share sheet.
The script
The aggregator runs on form submit. Bind this script to the Form (or to its
response sheet) so the onFormSubmit trigger has somewhere to attach.
// The non-sensitive rollup spreadsheet — counts only, safe to share.
const AGGREGATES_SHEET_ID = '1abcAggregatesId';
// The Form question whose answer identifies the person being reviewed.
const SUBJECT_QUESTION = 'Who are you reviewing';
/**
* Form-submit trigger. Increments the running counts for the reviewed
* teammate on the Aggregates sheet — without ever copying the comments.
*
* @param {Object} e The form-submit event Apps Script passes in.
*/
function onFormSubmit(e) {
// 1. Bail out if the event or the subject answer is missing.
if (!e || !e.namedValues || !e.namedValues[SUBJECT_QUESTION]) {
Logger.log('No subject in submission — nothing to aggregate.');
return;
}
// 2. Read who this review is about.
const subject = e.namedValues[SUBJECT_QUESTION][0];
// 3. Open the Aggregates sheet and read its current rows.
const sheet = SpreadsheetApp.openById(AGGREGATES_SHEET_ID).getSheets()[0];
const values = sheet.getDataRange().getValues();
// 4. Find an existing row for this subject (column A).
const row = values.findIndex((r) => r[0] === subject);
if (row === -1) {
// 5a. First review for this subject — start their counts at 1.
sheet.appendRow([subject, 1, 1]);
} else {
// 5b. Existing subject — bump both counts and write the row back.
values[row][1]++;
values[row][2]++;
sheet.getRange(row + 1, 1, 1, 3).setValues([values[row]]);
}
Logger.log('Aggregated a review for ' + subject + '.');
}
How it works
onFormSubmitfirst checks the event and theWho are you reviewinganswer exist — a malformed submission is logged and ignored rather than throwing.- It pulls the subject’s name out of
e.namedValues, which maps each Form question title to its answer. - It opens the
Aggregatesspreadsheet — the counts-only one, deliberately separate from the raw responses — and reads every existing row. - It searches column A for a row already belonging to this subject.
- If there is no row yet, it appends a new one with both counts at 1. If a row
exists, it increments
strengthsCountandimproveCountand writes the row back in place.
Crucially, the comment text in Strengths and Areas to improve is never
copied here. Only counts move to the shareable sheet; the comments stay on the
restricted response sheet.
Example run
Three colleagues submit reviews for Priya Shah. After the first submission,
the Aggregates sheet reads:
| subject | strengthsCount | improveCount |
|---|---|---|
| Priya Shah | 1 | 1 |
After the third:
| subject | strengthsCount | improveCount |
|---|---|---|
| Priya Shah | 3 | 3 |
Priya — and her manager — can open this sheet and see that three reviews are in, without any way to read the comments or work out who wrote them. The actual feedback stays on the HR-only response sheet.
Trigger it
This must run on a form-submit trigger, not a schedule:
- In the bound script’s editor, open Triggers (the clock icon).
- Add a trigger for
onFormSubmit, choose event source From form (or From spreadsheet if bound to the response sheet), and event type On form submit. - Approve the authorisation prompt the first time.
A simple onFormSubmit will not have the permissions to open another
spreadsheet, so the installable trigger above is required.
Watch out for
- Anonymity depends on access control, not on this script. Lock the raw response sheet down to HR only — if reviewers can open it, the whole exercise collapses.
strengthsCountandimproveCountalways move together here, so they are effectively the same number. If you want them to differ — for example, counting only non-blank answers — read each field and increment conditionally.- Concurrent submissions can race. If two reviews land at once, both may read
the same starting count and one increment is lost. Wrap the read-modify-write
in
LockServiceto make it safe. - The script matches subjects by an exact string. If the Form lets reviewers type a name freely, “Priya Shah” and “Priya Shah” become two rows. Use a dropdown of fixed names instead.
- Very small review groups erode anonymity on their own. If only one person reviews a teammate, the count of 1 plus the comment effectively identifies the author — set a minimum number of reviewers before sharing anything.
Related
Trigger an onboarding sequence on form submit
Kick off tasks when a new Northwind hire submits their starter form.
Updated Oct 17, 2025
Build a content-submission queue
Collect Northwind guest posts or ideas for review through a Form.
Updated Oct 9, 2025
Score sentiment in open-text feedback
Rate Northwind feedback comments without manual review — using the in-Sheet sentiment function.
Updated Oct 5, 2025
Build a peer-nomination and voting system
Collect and tally Northwind nominations for awards or initiatives — one ballot, anonymous.
Updated Oct 1, 2025
Roll a form over each cycle
Archive old responses and reset for the next Northwind cycle — quarterly OKR check-ins.
Updated Sep 27, 2025