Software Engineer · Licensed Electronics Engineer · Remote, Philippines

Cherljohn Neo
Rondovio

I build the software that sits between systems that were never designed to talk to each other: a POS and a WiFi controller, a Discord gateway and a Postgres database, four ad platforms and one spreadsheet. Then I ship the apps that come out the other side.

On the Microsoft Store
1 app
Python scripts in production
36
Client servers automated
20
Platforms integrated
8

About

Most of what I build runs unattended against live business systems, where a bad assumption costs real money and there is no staging environment to hide in.

That has shaped how I work. I write the unglamorous safeguards first: idempotency keys so a retried sale never mints a second voucher, ordering rules so a script can never wipe a spreadsheet before it knows it can refill it, blank-by-default targets on anything destructive so it cannot run until a human consciously picks one. I pick architecture to fit real constraints rather than to fit a diagram: a free tier's instance-hour budget, a controller's hard voucher ceiling, a platform with no API at all.

I work across Python and JavaScript/TypeScript: browser automation and Google Sheets pipelines on one end, Electron desktop apps, Capacitor Android builds, Cloudflare Workers and Postgres on the other. I document as I go, because most of these systems get handed to someone else to run.

Experience
Media-buying agency since April 2025, where I built the reporting automation, the Discord bots and a desktop app for the VA team on my own initiative
Licences
Electronics Engineer (ECE) & Electronics Technician (ECT), PRC 2026
Education
BS Electronics Engineering, Mindanao State University, 2020–2024
Recognition
4th Runner-up, Datathon at the IECEP National Students' Summit, 2023
Languages
English (professional) · Filipino · Cebuano · Hiligaynon

Selected Work

Grouped by what each one demonstrates. Several of these run against live production systems, so what follows is the architecture and the thinking behind each one rather than a tour of the code.

01

Shipped Products

End-user apps built, packaged, signed and released.

Flicker reviewing a photo in full screen with keep and delete controls
Flicker Delete Mode
Flicker session summary overlay

Flicker

Published

Desktop app · Windows · Electron · Solo build

A local media triage app. One photo or video fills the screen, you make one decision (keep, delete, or move), and the next file appears. Nothing leaves the machine.

The problem

Camera rolls and download folders accumulate thousands of near-duplicate photos and screenshots. File explorers are built for browsing, not for deciding, so the cleanup never happens.

What I built

A full-screen, swipe-style flow driven by arrow keys, on-screen buttons or a swipe. Deleted files move to a _deleted subfolder rather than vanishing, every action is undoable, and each session ends with a tally of what was kept, moved and deleted.

Engineering worth noting
  • Video seeking that actually works. A custom flicker-media:// protocol in the Electron main process serves media with HTTP Range support, so scrubbing a large video is instant instead of waiting on a full file read.
  • Locked-down renderer. contextIsolation and sandbox both on, with exactly four IPC calls exposed through a contextBridge preload. The main process is the only thing that ever touches the filesystem.
  • No framework, no build step. The renderer is a single static HTML/CSS/JS file, which is why the packaged app is small and starts instantly.
  • Three release channels from one codebase. An MSIX package for the Microsoft Store, an NSIS installer, and a portable single .exe.
  • Electron
  • Node.js
  • JavaScript
  • IPC / contextBridge
  • electron-builder
  • MSIX
Suaver app icon
Suaver brand kit: app icons and adaptive Android assets generated from a single source

Suaver

In development

PWA + Android · Vanilla JS · Capacitor · Solo build

A budgeting app that splits your money into days and shows one number: what you can spend today without touching tomorrow.

The problem

Budgeting apps report in months. People spend in days. The useful question is not "how much is left this month." It is "how much can I spend right now and still be fine."

What I built

Local-first: it works fully on the device with no account, and an optional account keeps a synced copy in the cloud. It runs as a web page, installs to the home screen, and ships as an Android build with a home-screen widget.

Engineering worth noting
  • Timezone-correct date maths. Real instants are mapped into a pseudo-UTC domain carrying the user's chosen wall clock, so all calendar arithmetic runs on UTC getters and behaves identically regardless of the browser's own timezone. Every function is pure and takes the timezone as an argument.
  • Storage as a driver stack. A repository layer owns the shape on disk (schema version and migrations), while a driver layer owns the place, picking the first available of local → bridge → memory and falling back to memory if one fails mid-session.
  • Strict layering. The core has no DOM and no rendering, so the money, period-rollover and timezone logic is unit-testable in plain Node. Dependencies only ever point downward.
  • Tests that don't rot. The suites assert relative to today rather than against hard-coded dates, so they stay valid as real time moves on.
  • A theme system from two numbers. Six colours × two modes, where a theme sets only a surface hue and an accent hue and the stylesheet derives the rest. The one exception is the signature brand palette, which stays literal so it can never drift as the generator changes.
  • JavaScript (ES modules)
  • Vite
  • Capacitor
  • Android
  • PWA
  • Supabase
  • node:test
  • CSS design tokens
02

Systems & Integrations

Services that keep running when nobody is watching.

Cafe voucher service architecture A point-of-sale system and a cashier page both call the voucher service, which talks to the Omada controller, a thermal printer, and a background janitor process. Loyverse POS receipts webhook Cashier page plain HTTP Voucher Service Omada controller site A · site B Thermal printer ESC/POS · port 9100 Janitor purges spent codes idempotencyKey = receipt line id a retried sale returns the same code, never a second voucher

Cafe Voucher Service

Middleware · Node 22 + TypeScript · Client project

Middleware that issues a WiFi hotspot voucher at the exact moment of sale, prints it, and quietly deletes spent ones, replacing a manual loop the cafe staff were running by hand every week.

The problem

The old routine: generate 500 vouchers, print them, hand them out, hit the controller's hard 5,000-voucher ceiling, delete the used ones by hand, and generate another batch. Every step manual, and the ceiling turned into a full stop mid-shift.

What I built

A small service between the POS and the TP-Link Omada controller. One endpoint issues a code for a sale and returns a ready-to-print slip; a background janitor purges spent vouchers so the cap is never reached. It covers two sites, plan lookups for the POS button layout, and reprints for lost slips.

Engineering worth noting
  • Idempotency as the core design constraint. The POS sends the receipt line id as an idempotency key, so a timeout-and-retry returns the same code rather than minting a second voucher. That matters because Loyverse delivers receipt webhooks twice as a matter of course.
  • A simulated controller. The whole flow runs end to end with no hardware and no cloud account, so development and the test suite never touch a live cafe. Going live is one environment variable plus a documented verification pass.
  • Tested against failure modes, not the happy path. The suite covers the 5,000-voucher cap recovery, duplicate webhook delivery, POS retries, and an internet outage mid-shift.
  • Zero runtime dependencies. It runs on stock Node 22 with nothing to install on the cafe's machine, so there is one less thing to break at 7am. Playwright appears only in the separate live-ops tooling.
  • Direct ESC/POS printing to a network thermal printer over raw TCP, with the slip layout verified against a real 58mm reference capture.
  • Node.js 22
  • TypeScript
  • REST API design
  • Webhooks + HMAC
  • TP-Link Omada API
  • ESC/POS
  • Playwright
  • node:test
Discord bot cloud stack Discord traffic splits into a Cloudflare Worker for slash commands and a Render service holding a persistent gateway connection, which writes to Neon Postgres and mirrors to Google Sheets. Discord one application HTTPS POST gateway socket Cloudflare Worker /onboard command never sleeps Render · one service voice / camera audit join alerts one client, two handlers Neon Postgres + Google Sheets mirror Config mirror 60s refresh, no redeploy ~750 free instance-hours vs a ~730 hour month → one service, not two

Discord Bot Cloud Stack

24/7 in production

Distributed service · Python · Cloudflare Workers + Render + Neon

The Discord bots I built for a media-buying agency's client servers, moved off a Windows tray app and onto always-on cloud infrastructure, running entirely on free tiers.

The problem

Three bot behaviours I run across the agency's client servers (client onboarding, voice and camera activity auditing, and new-member alerts) lived in a desktop script with a tray icon. If that PC slept, the bots stopped, and the audit trail was a text file nobody could query.

What I built

A two-host deployment behind a single Discord application, with the audit trail moved into a real database that reporting reads directly, so the report is always current with no manual export step.

Engineering worth noting
  • Split by hosting requirement, not by feature. The /onboard command only needs Discord to reach an HTTPS URL, so it went to Cloudflare Workers, which never sleeps, so the team never sees "application did not respond" behind a 60-second cold start. Voice state changes and member joins have no webhook and no REST endpoint on Discord's side; they arrive only over a persistent gateway socket, which is precisely why that half cannot be serverless.
  • Two features, one process, on purpose. The free tier gives roughly 750 instance-hours against a ~730-hour month, so a second always-on service would not fit the budget. Both gateway features became two sets of event handlers on a single client.
  • Blast-radius control. In a shared process, one server's missing config used to raise SystemExit and take the voice audit down with it. Now a missing channel disables that one feature for that one server, and everything else keeps running.
  • Config without redeploys. Server, prefix and buyer mappings are pulled from a mirror on a 60-second refresh, so changing one is a JSON edit that lands on its own.
  • A migration with a way back. The original desktop scripts were left untouched and documented as manual fallbacks, with an explicit runbook rule: suspend the cloud service first, or every event gets recorded twice.
  • Python
  • discord.py
  • Cloudflare Workers
  • Render
  • Neon Postgres
  • SQL
  • Google Sheets API
  • WebSockets
03

Automation & Data

Pipelines that replace hours of manual work every morning.

Daily refresh orchestration One command runs three independent scraper lanes in parallel; they join, and a final snapshot step runs only if every lane succeeded. One command the whole morning refresh Shopify partner stores Automize client dashboard Discord capacity + drives join: did every lane pass? ALL DATA snapshot skipped if any lane failed a half-rebuilt source never overwrites a good copy

Media-Buying Ops Toolkit

Daily in production

Automation suite · Python · 36 scripts · Built on my own initiative

Internal tooling I built for a media-buying agency: revenue and ad analytics pulled from four platforms into Google Sheets, Discord client-server reporting and maintenance, and a single command that runs the whole morning refresh.

The problem

Reporting spanned Meta Ads, Shopify, Omnisend and Postscript, plus a Discord workspace of client servers, each with its own login, its own export and its own format. Hours of copy-paste before anyone could look at a number.

What I built

A toolbox of independently runnable scripts sharing one config layer, plus an orchestrator that runs the daily refresh in parallel lanes and reports on all of them at once. Where a platform offers an API I use it; where it does not, a persistent browser session drives the real UI.

Engineering worth noting
  • A dependency-aware parallel run. Three lanes are genuinely independent and run concurrently. The fourth step is a snapshot of what the others just wrote, so it waits for the join, and is skipped entirely if any lane failed, so a half-rebuilt source can never be published over a good copy. The run summary names the exact re-run command.
  • An ordering rule learned the hard way. A scraper that cleared its sheet and then discovered it was logged out emptied 1,104 rows and wrote nothing back. The fix generalised: everything that can abort a run (the session check, the client list fetch, all of it) happens before the destructive clear, never after.
  • Safety interlocks on destructive tools. The scripts that delete channels or messages ship with a blank target by default. They cannot run until a human consciously names one; the blank is the interlock, not an oversight.
  • Silent failures made loud. After a platform redesign, one scraper read 33 of 626 stores and still reported success. Fixing the parser was half the job. The run now fails outright if a page links anything the parser did not return, so the next redesign stops the run instead of quietly shrinking it.
  • From checking to fixing, safely. A manual check of every client's SMS marketing workflows against the agency's standard became a script, then one that makes the missing workflows, fixes the wrong ones and turns on the stopped ones through the platform's own web API. It previews by default, changes a store only when the live platform agrees with the sheet, checks each new workflow as a draft before switching it on, and never stops a store's old welcome flow until the new one reads as running.
  • Documented for handoff. A single orientation document plus per-area runbooks cover what each script does, which decisions look wrong but are deliberate, and what to do when the cloud stack is down.
  • Python
  • Playwright
  • Google Sheets API
  • Meta Marketing API
  • Shopify API
  • Omnisend
  • Postscript
  • Concurrency
  • Service accounts
Self-update channel for the VA desktop app Bumping the version makes GitHub Actions build the installer and publish a release. Every open copy checks the latest release once a minute, offers one update button, verifies the download and hands over to the installer, which replaces the app and keeps the user's login. Push VERSION bump + git push GitHub Actions builds Setup.exe Release v1.0.x installer + SHA-256 Every open copy HEAD /releases/latest once a minute UPDATE NOW download, verify the checksum Installer replaces the app login kept checks the latest release, not VERSION a version whose installer is still building never locks anyone out one request to the website's redirect, not the rate-limited API

VA Team Desktop App

Released

Desktop app · Windows · Python · Self-updating

A desktop workplace for a media-buying agency's virtual assistants: one place to run the automations I build, which picks up every new one or fix the moment I release it. Version 1 carries one job: opening every Shopify store they collaborate on, under their own account.

The problem

Shopify grants store access per person, not per team, so each virtual assistant has to open more than 600 collaborator stores under their own account before a direct admin link works for them. The script that did it lived in a terminal, in a private repository.

What I built

A desktop app with a login gate and one run button. A pill shows whether the assistant's own Shopify session is live, the run stays disabled until a check has seen one, and each run ends with a report of the stores that need attention. It installs per user with no admin prompt, and updates itself.

Engineering worth noting
  • Updates nobody has to think about. Bumping the version builds the installer on GitHub Actions and publishes a release. Every open copy notices within a minute, locks behind one update button, downloads the installer, checks its SHA-256 and hands over; the installer waits for the app to close, replaces it and reopens it. The login lives outside the program folder, so it survives every update, and a run in progress finishes first.
  • A sign-in loop, measured rather than guessed. Shopify's sign-in kept sending an assistant back to the same page. A first fix did not hold, so I measured: the same fresh profile, with and without Chrome's debugging port. The sign-in silently rejects any Chrome with a debugging connection, which is exactly how browser automation drives it. The login window is now an ordinary Chrome followed only by its window title, and a separate check confirms the session took.
  • A pinned toolchain. The first release asked CI for "Python 3.14", got a point release bundling Tk 9, which reports widget sizes in different units, and every card broke. The fix pinned the exact Python in CI and in the local build script, which now refuses any other.
  • One task at a time, by design. Every task runs as its own worker process and reports to the window over a line-based JSON protocol, because Chrome allows one process per profile and a second launch would kill the first mid-run.
  • Python
  • tkinter
  • Playwright
  • PyInstaller
  • Inno Setup
  • GitHub Actions
  • GitHub Releases
  • Win32 API
04

Also Built

Smaller projects, prototypes and one-off client work.

Budget Tracker

Vanilla JS · Supabase · Personal project

A local-first budgeting app with cloud sync. Sync compares each side against the last stamps both agreed on, never a phone's clock against the server's, so a device running slow can never lose its own edits. A fresh, empty install can never overwrite real data, and the server keeps one level of undo.

  • JavaScript
  • Supabase
  • Row-level security
  • node:test

CAFeeeyyy

Native Android · Kotlin · Client project

A barista training app I built on my own initiative for my cafe clients, so new hires can learn the menu: each drink's recipes are set up once, and a practice mode quizzes them on a random drink and scores the answer. Everything stays on the phone.

  • Kotlin
  • Jetpack Compose
  • Material 3
  • Android

Split

React 19 · TypeScript · Tailwind 4

A bill-splitting web app built from a design handoff. Written as a fully commented codebase alongside a "React for Python developers" guide, deliberately structured as a teaching artefact as well as a working app.

  • React
  • TypeScript
  • Vite
  • Tailwind

Captive WiFi Portals

Two cafe venues · HTML/CSS · Python

Branded captive-portal login pages for two venues, plus a Python generator that turns a raw voucher export into a print-ready sheet of cut-out codes. It was the manual predecessor to the voucher service above.

  • Python
  • HTML/CSS
  • Omada portal

Raspberry Pi Camera Server

Python · Embedded Linux

A streaming camera server for a Raspberry Pi with a companion viewer client and link-quality probing tools, built to get a live feed off a headless board over an unreliable connection.

  • Python
  • Raspberry Pi
  • Video streaming

Skills

Languages

  • Python
  • JavaScript (ES modules)
  • TypeScript
  • Kotlin
  • SQL
  • HTML & CSS

Application

  • Node.js
  • Electron
  • React
  • Vite
  • Capacitor / Android
  • Jetpack Compose
  • Progressive web apps
  • Python desktop apps
  • Installers & self-update

Cloud & Infrastructure

  • Cloudflare Workers
  • Render
  • Neon Postgres
  • Supabase
  • Google Cloud service accounts
  • GitHub Actions
  • Free-tier capacity planning

Integrations

  • REST API design
  • Webhooks & HMAC signing
  • Meta Marketing API
  • Shopify
  • Discord gateway + HTTP
  • Google Sheets API
  • TP-Link Omada
  • ESC/POS hardware

Automation

  • Playwright browser automation
  • Scheduled & unattended jobs
  • Idempotency & retry design
  • Job orchestration and concurrency
  • Data pipelines into Sheets

Practices

  • Testing failure modes, not happy paths
  • Simulators for hardware-dependent work
  • Local-first sync design
  • Runbooks & handoff documentation
  • Safety interlocks on destructive tooling
  • Git

Contact

Open to project-based and fixed-rate work in automation, integrations, backend services, and app development. I'm reachable for calls during Philippine daytime, any day of the week, and most active during US Eastern business hours. If something here is relevant to what you are building, get in touch.