Author: Ryan Tse

  • The Rise of AI

    As an engineering leader, I encourage my reports to responsibly use AI to speed up their work. There’s great value in automating mundane coding by delegating the work to AI. I trust them to be able to evaluate the output from AI and to reason about the correctness of the code being generated. Thus far, I have seen it be a boon to my developers. However, the explosion of AI has started showing up in different areas, ones that are challenging to address as a remote company.

    I have recently seen an explosion of AI usage within the hiring process and has inserted a sense of uncertainty when screening and interviewing candidates. I have started seeing resumes that have been produced using AI by taking the job description and rewording the candidate’s description of their most recent role to be an exact match to the job description. Firstly, this is clearly dishonest as they are most certainly not doing 100% of the exact same thing. It may get past an ATS keyword search, but it will not pass muster when it comes to hiring manager review. While this may get their resume to be seen by me, this isn’t the most egregious since I haven’t necessarily wasted significant time interviewing the candidate.

    The more egregious behavior that I have seen and feel that it’s an irresponsible usage of AI is during the actual interview process. You can argue that interviews are not necessarily the best way to determine qualifications of a candidate, but equally, dishonesty is a quality that no company wants. Discretely using AI during an interview falls squarely in that camp. Currently, the AI interview assisting technology is still in their nascent stages and that still allows interviewers to catch inhuman-like behavior. The more recent examples are linear development of code in coding interviews — nobody writes code perfectly linearly the first time around, there’s almost always edits to the code as you write it. The second is ultra-detailed responses to questions, with a higher-than-normal degree of detail in every sentence. These are tell-tale signs that I am relying on as an interviewer to determine potential dishonesty, but it’s unfortunately a matter of time before the AI technology is tuned to behave more human-like.

    It’s not clear to me currently how we will deal with this rise of AI. Back to in-person interviews? This seems to only work for companies that aren’t fully distributed and limits the talent pool significantly. Testing centers for interviews? Similar to getting a certification, maybe the answer is that they will need to show up somewhere to conduct the interview. Ultimately, only time will tell, but it feels like a never ending race.

  • The Great Migration: We’re Back

    After a long hiatus from writing blog posts, we are I am back at it! I finally found the (monetary) motivation to fix this blog up and move it to a new, lower-cost home (while of course, first spending a bunch of money doing so). Ultimately, the savings will be well worth it and what better way to come back to blogging than to talk about how I accomplished this move.

    Historically, this blog has always been hosted on a rented VPS. I recently saw the bill for the latest month and decided that it was time to bite the bullet and move this blog to cut down on needless cost that was delivering a half-broken, sometimes fully broken, unmaintained blog.

    Things have come a long way since I first started hosting this blog. Initially, my main drivers for using a VPS were:

    • Network connectivity – with my paltry 30 Mbps upload speed, I was not about to waste it on hosting content from home
    • Security – I didn’t want to expose this blog directly from my home IP to the internet

    So, what’s changed since then and how do we address these issues? Technology has advanced significantly in the last few years, especially in the private/reverse tunnel tech, also I’ve since gotten better networking equipment, better internet, and more experience running services within a secure environment.

    Let’s start with a simplified overview of what the configuration looks like now:


    Egress Networking

    With this updated architecture, the system is isolated into its own dedicated, isolated LAN. This LAN sits firewalled away from all other traffic. Additionally, the system is configured with routing rules that ensure all internet-bound traffic from the DMZ LAN is sent through a Wireguard VPN tunnel. Even if a compromise were to happen, traffic would not be able to originate from my home network IP range, instead exiting via a VPN exit-node IP address.

    Ingress Networking

    For readers like you, to provide access to this blog, I’ve leveraged Cloudflare’s CF Tunnels to provide a reverse-proxy into the DMZ LAN. This allows me to expose specific services hosted on the Raspberry Pi without opening a listening port to the world. For added convenience, Cloudflare also supports TLS out of the box – no more needing to remember to renew certs.

    Additionally, in this design, I’ve deployed a Tailscale VPN so that I can use an overlay network to access the Raspberry Pi sitting in the DMZ LAN. This ensures that I don’t need to have additional firewall rules that poke holes from the DMZ LAN to any of the other VLANs. Instead, when I need access to the host, I can just sign on to the VPN via SSH. Additionally, instead of having to deploy a public key to each host, access is governed via Tailscale SSH, allowing me to dynamically write ACLs that allow access to the hosts.

    OS Provisioning

    I wasted more time than I am willing to admit on this area. Wanting to make things easily reproducible, I started with a simple goal: use cloud-init to provision the node running Ubuntu 24.02 LTS. What I ended up with was headache after headache with how cloud-init functions and the limitations with how cloud-init interacts with a Raspberry Pi.

    The issues boil down to one simple thing: cloud-init was designed for the cloud, and Raspberry Pi’s are definitely not the cloud. Most security underpinnings today are based upon the concept of time, especially when it comes to CA certificates and other security signatures. This brings us to an interesting issue, with a brand new Raspberry Pi in hand, there is no hardware clock to seed time to the operating system. What this translates to effectively is that most things like setting up new apt repositories, or fetching the signing key for packages, or making any sort of TLS-enabled cURL call will fail. Due to the way cloud-init functions, unless you have a RTC clock installed (which, with the Raspberry Pi 5 you now can, but you’d still need to figure out a way to initialize this clock), you will find that cloud-init falls over itself endlessly.

    This ultimately took significant time to debug, largely because you’d think it should work, so I spent hours searching for a workaround, only to arrive at the conclusion that it wasn’t possible to achieve within the confines of cloud-init’s stages. Ultimately, the ugly solution is to basically cannibalize the usage of cloud-init. I effectively wrote my own bash script to run at the end of the cloud-init cycle, after the system’s clock has successfully synced with an NTP server, which does all of the fetching keys, installing packages, and setting up services.

    Docker + Docker Compose

    Now that the system is finally booted and connected to the Tailscale network, it’s time to setup some services! Now you may be wondering, why only Docker and Docker Compose? Why not Kubernetes? While Kubernetes is powerful, there isn’t much need currently for leveraging Kubernetes in this environment and it adds an extra level of abstraction that currently isn’t needed for my setup and likewise consumes precious resources that are limited with the Raspberry Pi-based environment.

    I leverage a simple Docker compose file which sets up the necessary containers to compose together this WordPress site. The site runs as a container bound to the local host. This is then connected in with the Cloudflared service running on the host and allows a reverse-proxy to serve content from the blog directly to the internet.

    Next Steps

    Now that the blog is functional again, there’s a few housekeeping tasks remaining – top one being backups. With the Raspberry Pi on the DMZ LAN, there is no access to internal backup systems. Instead, I will look at enabling S3-based backups to my existing backup cloud storage provider to enable periodic backups of this blog.

  • using namespace std;

    Today, I was tutoring a classmate for CS16, one of our beginning CS classes at UCSB. One of the first things that almost all the CS professors teach students to begin their C++ code with is using namespace std;. However, this is often without explanation of what is happening behind the scenes and I would argue that this is a horrible practice which leads students to have hidden bugs in their code.

    To illustrate the problem with this, allow me to illustrate with an example of how using namespace std; leads students down a path of potential confusion. In the class, they were learning about pass-by-value, pass-by-pointer, and pass-by-reference. In the class, the following code was given:

    #include <iostream>
    using namespace std;
    
    void swap(int *px, int *py) {
        int tmp = *px;
        *px = *py;
        *py = tmp;
    }
    
    int main(int argc, char **argv) {
        int x = 10, y = 20;
        cout << "Before:" << endl << x << " " << y << endl;
        swap(x, y);
        cout << "After:" << endl << x << " " << y << endl;
    }
    

    Now, if you noticed the way that swap is being invoked by main, you would quickly observe that this should be a compiler error since the swap function is expecting an int *, and you have passed it an int. However, if you run the code through a compiler, “magically” it works and you get the values that you expected!

    What happened in the compiler was that the code utilized a built-in version of swap from std, making it “work”. This is only one simple example of how an error like this can confuse students and how having students blindly insert using namespace std; is a huge mistake waiting to happen.

    Thus, I think it is a very bad idea to teach students to blindly use using namespace std; without understanding what it does to the students’ code. There are many functions in the std namespace, and it makes it extremely hard to detect errors in examples like this. And, in all honesty, learning to prefix things like cout and use std::cout is good practice and enhances students’ understanding of their code.

  • Single Transferable Vote Election Tabulator

    Single Transferable Vote Election Tabulator

    For the 2016-17 school year election, the University of California, Santa Barbara’s Associated Students adopted single transferable voting¹ to elect their senate seats. Leading the software development, the tabulator is implemented in Python 3 with a wxPython-based front end. The software provides support for custom parsers to convert ballot data from different polling software to a universal JSON data format which is then processed by the tabulator. The software is divided into a backend thread that processes the election tabulation data and a modular front-end to allow for future alternative front-end implementations.

    As of December 2016, the election tabulator using STV has been open sourced and is available on GitHub. It is currently in use by the University of California, Santa Barbara, and North Dakota State University.

    ¹ Single transferable voting is a ranked voting system that redistributes an elector’s vote among unelected candidates based upon the individual’s ranked preference when either a candidate has been elected or eliminated from a race. This system provides a more proportional representation of parties during a multi-seat election.

  • CloudFlare Bulk IP Switcher

    A little while back, I had to move a bunch of web services that were hosted on a VPS to a new datacenter. As part of the move, I wasn’t able to transfer the IPs, so I was assigned a new IP. So, now I had to update all of my DNS records to reflect the change. Owning multiple domains, I realized that CloudFlare doesn’t provide an easy way to make this IP change across multiple zones (domains).

    If I wanted to change the records in each zone, I would need to export the DNS records in BIND format, make the changes, and then reupload the records. To fill this gap, I created a simple bulk IP changer that uses the CloudFlare API to iterate through all the zones and find all instances of the original IP address and replace the value with a new IP. I recently had the time to clean up this code, and I have now released it as open source.

    The source code is available on GitHub under an MIT license.

  • Tic-Tac-Toe Robot

    Tic-Tac-Toe Robot

    As part of ECE 92 (Projects in Electrical and Computer Engineering), I designed and built a robotic arm that plays a pencil-and-paper tic-tac-toe game with either 1 or 2 players. The system consists of an ARM based Arduino MCU board, a light weight 3 servo robot arm modified to hold a pen that floats vertically to handle variations on the board and arm angles, an LCD that displays the score, and two keypads for user input. The C++-based embedded software calibrates the arm servos which then draws the 9-square game grid, scans the keypads, updates the display, and draws the X and O game symbols based on the user input or its internal game algorithm.

    The display poster is available here and the related source code can be found on GitHub.