Shahria Emon
Biology Aid · Client build · Live

No login, no database. Orders land in a Google Sheet.

A one-page store for a biology teacher's book and video course, built for students on mobile data, and run from a spreadsheet he already knew.

biologyaid.com first screen on a phone: book cover, headline and order button
Order form with four product cards showing all-in prices
Order confirmation with an order number (sample order, not sent)
1
request before the page appears
23.5KB
the whole page, compressed
0.06s
server response from the edge
146
automated checks at handover
In 30 seconds
The problem
Sell a book, a 20-class video course and a bundle to admission students on their phones, for an owner whose whole toolkit is a Google Sheet and Google Drive.
What I did
Cut the planned login and member area, used Drive's per-email sharing instead, and built one fast page whose orders flow through Cloudflare into his sheet.
The outcome
Live since August 2026 and taking real orders, with three post-launch fixes driven by real buyers.
Client Al Mahi Rahman, Biology AidRole Spec, build, deploy, supportLive August 2026Stack Cloudflare Pages, Apps Script, Sheets
01The brief

Three products, one page, a phone in every hand.

Students arrive from a Facebook ad or a WhatsApp link, on mobile data, and decide within seconds.

Al Mahi Rahman teaches biology to students preparing for Bangladesh's university admission exams. He sells Last Hope Biology 2.0, a printed 100-page book covering all of Botany and Zoology, a 20-class recorded course, and a bundle of both. Payment in Bangladesh still happens by phone, through bKash, Nagad or cash on delivery, so the site takes no money at all: a student leaves a name, number and Gmail address, and the owner calls to confirm, ships the book and shares the course on Google Drive.

02The key decision

The best login was no login.

The first spec had a full member area. I cut it before writing code, and the owner ended up with more control, not less.

An unlisted YouTube link can be watched, downloaded and reshared by anyone who has it, and access cannot be withdrawn from one viewer. A login in front of it protects the page, not the video. Google Drive sharing is per email address: the student has to be signed into that exact Google account, and access can be revoked for one person without touching anyone else. Nothing to build, nothing to host, no monthly cost. It is also why the form asks for a Gmail address and gently warns when one does not look like it.

Removed
  • User accounts and login
  • Password reset
  • A database
  • A transactional email service
  • A member area
Kept
  • A fast page
  • An order form
  • A Google Sheet the owner already uses
  • Course access by Drive, per email
03Speed as a hard rule

One request before the first paint.

For a student on a weak connection, the first impression is the load time, so speed was written into the spec as rules the build had to pass.

All CSS sits inline. All critical JavaScript sits inline, capped at 11.5 KB. There are no web fonts: system fonts render Bangla natively and cost nothing. No framework, no bundler, no npm package reaches the page. Everything below the fold waits: the video list loads on idle, thumbnails are requested only near the screen, and the YouTube player is a facade with no iframe until it is tapped.

23.5 KB
The whole page as served.Brotli compressed, from Cloudflare's edge in about 0.06 seconds.
biologyaid.com
biologyaid.com desktop hero: Last Hope Biology 2.0 headline in Bangla with order buttons beside the book cover
The live page on desktop. Most visitors see the mobile layout.
04The order pipeline

From a phone to a spreadsheet row, safely.

The form posts to a Cloudflare function, not straight to Google, so failures are readable, the script URL stays private and bots are dropped at the edge.

  1. Validated twice, priced once

    The browser checks each field with errors in Bangla; the function checks again and derives prices on the server. A tampered price is ignored, not trusted.

  2. Phone numbers the way people type them

    +880, 880 and 00880 all normalise to the local 11-digit form, so a real number is never rejected for its format.

  3. Spam handled without losing buyers

    A honeypot, a minimum time to submit and a duplicate window. A suspiciously fast submission is saved and flagged, not dropped, because autofill makes real students fast too.

  4. Order IDs a human can read out

    Like AMR-261024-K7Q2, stamped in Dhaka time, so the owner and the student can refer to the same order on a call.

0.09 s
Sheet reads through the edge cache.Straight from Apps Script they took 3.4 seconds.
Need a fast site your client can run from a spreadsheet?No admin panel to learn, no database to pay for.
Talk it through
05Measuring the ads

Facebook tracking that cannot slow or break an order.

The Meta pixel never loads with the page, and nothing it does can stop an order.

Its roughly 100 KB arrives on idle or on the student's first scroll or tap, and every pixel call is wrapped so an ad blocker or Facebook being down can never touch the order flow. The server also sends the conversion through Meta's Conversions API after the row is in the sheet, and both copies carry the order ID as the event ID, so Meta counts one conversion, not two. The client verified it in Events Manager: browser and server events deduplicated, with email and phone matched.

06After launch

Real buyers found three things. Each got a real fix.

Every change after launch came from a real buyer or the client, and each was fixed at the cause.

Prices that did not add up in a buyer's headOne all-in price per card

The book showed 250 taka, then a delivery picker added 60 or 110, and students got confused about what they would pay. The book is now listed twice, inside and outside Dhaka, at 310 and 360 all in. Behind the page nothing changed: the sheet still records price, delivery and total separately.

Free delivery is still delivery"Ships" and "is charged" kept as separate ideas

When the client made bundle delivery free, the easy change would have stopped asking bundle buyers for an address and lost their books. The code keeps "this ships" and "this is charged" as two flags, in both the function and the sheet script.

A real order shown as failedA retry that turns into a confirmation, never a duplicate

A buyer in WhatsApp's in-app browser saw a red error while their order was already in the sheet: Apps Script had run past the 10 second timeout. The fix is a longer timeout plus one automatic retry with the same order ID, which the duplicate check turns into a confirmation instead of a second order.

Handover included an owner guide written for a non-developer, a README for the next developer, and 146 automated checks covering pricing, validation, phone formats and the tracking IDs.

Results

Fast, simple, and in the owner's hands.

Measured on the live site, or taken from the project's own records.

1
request before first paint
23.5KB
whole page, Brotli compressed
0.06s
server response from the edge
0.09s
sheet reads via edge cache, was 3.4 s
0
web fonts, frameworks or npm packages
146
automated checks at handover
1
Meta conversion per order, browser and server deduplicated
3
post-launch fixes driven by real buyers
Stack

What it runs on.

Front end
Hand-written HTML/CSSVanilla JSSystem fontsBangla-first copy
Edge
Cloudflare PagesPages FunctionsEdge cache + rate limit
Back office
Google Apps ScriptGoogle SheetsDrive per-email sharing
Growth
Meta pixel (deferred)Conversions APIJSON-LD Book + Course
Let's talk

Need a fast site your client can run from a spreadsheet?

Tell me what you're building, your timeline, and a rough budget, and I'll tell you honestly if I can help.

Thanks, your message is on its way. I'll reply within a day.
Shahria EmonCOO, Core Devs Ltd
Prefer email?
hello@shahriaemon.com
  • A reply within a day, from Dhaka (GMT+6)
  • An honest yes or no on whether I can help
  • 5/5 on Upwork with the Core Devs team
Next case study
7 days
Two years of patches. Seven days to rebuild it solo.
Scrumo, the project management app Core Devs runs on.
Let's talk