- Amogh's Hot Takes
- Posts
- Building a registration system with low-code tools (part 2)
Building a registration system with low-code tools (part 2)
Now with more code!

Registration system adjustments
Last post, I built a registration system for my choir with no-code tools. That concluded with having to write some code.
I presented the system to my choir director and in classic software development fashion, new requirements came up! Previously, my choir director Charlie was using Sawyer, a classes and activities platform. From that, she collected emergency contacts, allergy information, and photography consent.
My new registration system would need to do all the same. And like in Sawyer, ideally choir members could have a profile where all that information is saved and doesn’t need to be re-entered every time.
With my previous solution, I took a payment on Stripe and then added the email of the purchaser to the correct choir table. But the data provided by Stripe is minimal, I couldn’t even guarantee collecting the name of the purchaser.
Charlie was adamant that choir members need to fill out this information before providing payment to ensure it will be collected. So back to the drawing board!
Exploring form and registration apps
My initial thought was have an all choir members table where the emergency info was saved. The flow would look something like this.

There were two glaring problems with this flow:
There is a blatant privacy issue where you could enter anyone’s email in choir and get their personal info.
There aren’t any form apps that support making a query between form pages. The only hacky way to do it would require multiple forms.
It seemed like the only solution to get around this is to build all of these needed features from scratch; an absolutely terrible idea.

Thankfully, I found that Sawyer integrates with Zapier. An automation can trigger when a choir booking is made. Sawyer can continue being the platform for choir member data collection and payments.

UX adjustments
In this this talk, David Rusenko* discusses the importance of running UX testing sessions. Some of your assumptions will be quickly smashed by the user and that’s exactly what happened to me.
The most glaring issue was when Charlie tried editing the automations in Zapier—a few needed to be edited before every new choir cycle. She isn’t familiar with using automation platforms and it was daunting to modify and troubleshoot by herself.
I had recommended in my previous post to make automations modular and separate concerns. After UX testing, I decided it was a better tradeoff to revert to longer automations if it meant no one had to edit them.

Goodbye no-code, hello low-code
You might have noticed above that there is an automation task that involves written code! I had tried to avoid this, but it seems like Charlie may always be somewhat reliant on someone with software development skills. To address this, I’m being kept on retainer. And even if I’m hit by a bus, there will always be at least one software engineer to help who can trade some debugging time for free choir.
So I’m leaning into writing more code for this project, but I’m trying to write it as legibly as possible while keeping it modular and fault tolerant. Which is generally how I write code anyway.
Adding a check in system
Charlie had mentioned wanting to add a check in system via QR code in our first discussion, but like a responsible product developer, I wanted us to focus on the base requirements first.
But like an irresponsible product developer and blatantly ignoring the advice of David Rusenko I just linked to earlier, I got excited and started building a check in system without fully understanding user needs. Will Charlie like this check in system? I guess I’ll find out tomorrow.
The current process involves checking a box on an attendance sheet. I have no idea what Charlie does with these pieces of paper, but any manual data entry or tabulation sounds tedious.
Check in architecture
Most QR codes take you to a web page somewhere, so my solution was to create a check in form. That form leads to an automation that adds data to Airtable.

Requirements
When it comes to implementation details, I had a few requirements to work in:
The check in process should be as quick as possible
Choir members shouldn’t be able to save the web page address when not present; they should have to scan the printed QR code
Make it secure enough without it being too annoying
Make it fast
The fast issue requirement is easy: the form only prompts you for your email once. After that, it’s saved to browser storage. If the saved email is found on future check ins, the check-in request is made right away. Users won’t even see the form; just a confirmation.

Make it difficult to check in without the QR code
The other piece of data the form needs is the name of the choir you’re checking into. I don’t want this to be saved to browser storage as some choir members are a part of multiple choirs.
I instead have this saved to the link in the QR code as a query parameter. So choir members will have to use the QR code to sign in
https://www.choir-form.com?data=Metropop
If you try to check your browser history and visit the page without the query parameter, you’ll just see the following message: Failed to check in. Try using the QR code again.
Once you check in, the logic rewrites the navigation history, so you won’t be able to use the back button to get to the page. It’s not unexploitable, but it’s tough to check in by just the page link alone.
Make it (mostly) secure
Without creating a whole authentication system or middleware, the easiest way to prevent anyone from spamming the server url is security through obscurity. You won’t know where this webpage is hosted (it’s not at www.choir-form.com
). I’ve prevented the page from being indexed too, so you won’t find it via Google.
Choirs will be identified not by their name, but by a specific key. So even if an attacker finds this page with the server url, they won’t be able to write data without that exact right key and an email of someone in the choir.
Telling you this is not ideal for my security strategy. But the only people who have all the info to effectively spam the system are other software engineers in the choir. Don’t try anything funny Ajan, Ellen, and Hanson!
Deployment
For the server—which is responsible for receiving check-in data and sending it to Airtable—I discovered Zapier Functions. It’s a way to write an automation with code and some API connection utilities of Zapier. If you trigger the function with a webhook, it essentially becomes a serverless cloud function (although you can’t control response codes, the default is 200 success).
Figuring there would be enough complexity and custom actions, I chose Zapier Functions over Zapier’s UI builder.
choir_table = input['choir'] # Metropop_table
email = input['email']
date = datetime.now()
choir_table_id, table_has_date_col = find_table(choir_table)
if not table_has_date_col:
create_date_col(date, choir_table_id)
record_id = find_member_record_id(email, choir_table)
mark_attendance_for_member(record_id, choir_table_id, date)
add_row_to_attendance_table(email, choir_table, date)
Zapier’s Functions feature is still in beta with some bugs to work out. The Airtable API utilities have inconsistent APIs (notice how some of my functions require a choir_table
name and others require choir_table_id
) and are sometimes just blatantly broken.
In spite of this, it was still fairly easy to develop and deploy. I considered moving this fully to a cloud platform for cost savings and controlling the response status, but I think the conveniences and maintainability outweigh any additional compute cost we may incur with Zapier. I’ll just have to keep an eye on the logs for any unexpected errors.
So we’ll see how Charlie likes this registration system! I’ve made one graph in Airtable so far.

Appendix
Everyone should watch this incredible video on how QR codes work.
*David Rusenko and I overlapped at Square, but he was a director and I was a humble mid-level engineer. I never had a meeting with him and it would be bold of me to call him a former colleague lol.
Reply