Wednesday, 12 November 2025

Spot The Bug: The Underperforming Chart Scale

Hey, hey! Spot The Bug is back in town, and we're rolling out a new bug-fix story!

Can't wait to
squash another bug.

Working with D3 is one of my favorite pasttimes, especially when I have a new dataset to play with. However, there are some gotchas, and today's episode of Spot The Bug is a story of how one of these gotchas, well, got me.

I had some data that I wanted to plot a chart for. I truncated it a bit for brevity, but it will serve to illustrate the point. Note in particular, the column val.

datavals.csv
date,val
2024-11-01,8
2024-11-02,2
2024-11-03,17
2024-11-04,11
2024-11-05,25
2024-11-06,4
2024-11-07,7
2024-11-08,3
2024-11-09,8
2024-11-10,15


Here's the code I wrote to create an y-axis
<!DOCTYPE html>
<html>
  <head>
    <title>Chart</title>

    <style>
      svg
      {
        width: 800px;
        height: 500px;
      }
      
      .scaleTick, .scale
      {
        stroke: rgba(100, 100, 100, 1);
        stroke-width: 1px;
      }
      
      .scaleText
      {
        font: 8px verdana;
        fill: rgba(100, 100, 100, 1);
        text-anchor: end;
      }
    </style>

    <script src="https://d3js.org/d3.v4.min.js"></script>
  </head>

  <body>
    <svg>

    </svg>

    <script>
      d3.csv("datavals.csv", function(data)
      {
        var dataVals = [];

       for (var i = 0; i < data.length; i++)
       {
         dataVals.push(data[i].val);
       }

       var maxVal = d3.max(dataVals);

        var chart = d3.select("svg");
        chart.html("");

        chart
     .append("line")
     .attr("class", "scale")
     .attr("x1", "50px")
     .attr("y1", "50px")
     .attr("x2", "50px")
     .attr("y2", "450px");
    
        var pxPerUnit = Math.floor(400 / maxVal);
    
        var scale = [];
        for (var i = 450; i >= 50; i -= pxPerUnit)
        {
          scale.push(i);
        }
    
     chart.selectAll("line.scaleTick")
     .data(scale)
     .enter()
     .append("line")
     .attr("class", "scaleTick")
     .attr("x1", "40px")
     .attr("y1", function(d)
     {
     return d + "px";
     })
     .attr("x2", "50px")
     .attr("y2", function(d)
     {
     return d + "px";
     });
    
     chart.selectAll("text.scaleText")
     .data(scale)
     .enter()
     .append("text")
     .attr("class", "scaleText")
     .attr("x", "30px")
     .attr("y", function(d)
     {
     return d + "px";
     })
     .text(
     function(d, i)
     {
     return i;
     });     
      });
    </script>
  </body>
</html>

Here, I got a scale. However, something was wrong. The largest value in my dataset was 25. Why was the largest value here 8?


What Went Wrong

My first clue was here, near the top of the script where I declared maxVal. Because according to this line, max() was returning 8 as the largest value.
var maxVal = d3.max(dataVals);


This would explain why the scale started with the value 8 and worked its way down from there.

Why It Went Wrong

If the largest value in the dataVals array was 25 and the script thought the largest value was 8, there was one explanation for that. The script was comparing alphabetically rather than numerically. As text, "8" is always larger than "25". Basically, the dataVals array was being filled with strings rather than integers from the CSV file.
var dataVals = [];

for (var i = 0; i < data.length; i++)
{
  dataVals.push(data[i].val);
}

How I Fixed It

I used the parseInt() function to convert the data before pushing it into the dataVals array.
var dataVals = [];

for (var i = 0; i < data.length; i++)
{
  dataVals.push(parseInt(data[i].val));
}


And now it took 25 as the largest value! Because now dataVals was filled with integers rather than text, and it was comparing accordingly.


Moral of the Story

You'd think I would have learned to sanitize these things by now, especially in JavaScript, or any loosely-typed language. Apparently not!

Stay int-tellectual,
T___T

Thursday, 6 November 2025

Why Your Database Needs Audit Fields

In most relational database schemas, there exists a convention where timestamps and text strings are stored to record when a row was last inserted or updated. These are created in columns, and these columns are commonly referred to as Audit Fields.

Time for an audit!

Take for example this schema in MySQL, for the table Members.
CREATE TABLE Members (
    "id" INT NOT NULL AUTO_INCREMENT,
    "name" VARCHAR(100) NOT NULL,
    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "createdBy" VARCHAR(50) NOT NULL DEFAULT "system",
    "updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    "updatedBy" VARCHAR(50) NOT NULL DEFAULT "system"
);


The last four fields are Audit Fields:
- createdAt: this is a timestamp that is set to the current date and time when the record is created, and never changed.
- createdBy: this is a string that is set to the user's login when the record is created, and never changed.
- updatedAt: this is a timestamp that is set to the current date and time when the record is created, and changed to the current date and time every time the record is updated.
- updatedBy: this is a string that is set to the user's login when the record is created, and changed to the current user login every time the record is updated.

How do Audit Fields work?

"Created" fields. These are less useful because they should never be updated, and serve as a static record of when the record was created, and by who. However, it's good to have because it provides an initial reference point to troubleshoot if needed.
INSERT INTO Members (name)
VALUES ("Ally Gator");


id name createdAt createdBy updatedAt updatedBy
1 Ally Gator 2024-05-05 16:05:42 admin1 2024-05-05 16:05:42 admin1


"Updated" fields. These are updated when the record is created and every time the record is updated. While it's not as useful as a full audit log, it at least lets you know when the last update was. Let's say you run this query.
UPDATE Members SET name = "Allie Gator" WHERE id = 1;


id name createdAt createdBy updatedAt updatedBy
1 Allie Gator 2024-05-05 16:05:42 admin1 2024-05-16 12:27:21 admin1


Why Audit fields?

At the risk of stating the obvious, these are useful when you need to perform an audit (hence the name) on a database as to what records were created or updated, when, and by who. The importance of this only becomes increasingly obvious the larger the database grows.

Audit Fields can be a pain to set up, especially if you're not used to it. Once you've done it enough, though, you may find yourself wondering how you ever managed without them. Yes, they take up space. Yes, you have to bear them in mind during INSERT and UPDATE operations.

But man, they're awfully useful.

Examining data.

Imagine you need to do some forensics on some data that got added out of nowhere. You can check the timestamps and the ids of whoever created them. If they don't match any user on record, you know you have a problem. Scratch that - there's (almost) always a problem, but at least you have a better idea where it's coming from.

Or even if it's not a security breach, perhaps there's a dispute as to who was responsible for a certain data update? If the timestamps and user ids are right there in the database, there's instant accountability.

And all this is not even considering the fact that depending on the prevailing laws of the land, the presence of Audit Fields may even be mandatory.

Ultimately...

Get comfortable with the concept of Audit Fields. It's not new. It's pretty much timeless, in fact. It always represents some extra work at the start, but will save you so much hassle in the long run.

See you later, Allie Gator!
T___T

Saturday, 1 November 2025

The year I finally achieved that JLPT certification

Some say "better late than never". I got a huge dose of that this year as I obtained my Japanese Language Proficiency Test (JLPT) N5 Certificate.

Why was this overdue? Well, you see, because I actually began on this journey at the tender age of 15. I was a Polytechnic student at the time, and this was a supplementary module. At first, things went well. Having received education in Chinese, learning the basic sounds of Japanese was a breeze. Learning the Hiragana and Katakana writing systems wasn't much of a stretch as well; in fact it was significantly easier than reading and writing Chinese characters. Until we got to Kanji, which essentially was Chinese characters.

Ugh, more Chinese.

At which point I lost all interest.

Also, at this time, I had begun an obsession with writing code, and building stuff. Japanese was a pretty language, but it just didn't fit into my world the same way C++, SQL or even QBasic, did.

And the rest, as they say, is history. Even after graduation, not only did I neglect whatever little Japanese I had learned, the next few decades were a disastrous collection of bad career choices, even worse lifestyle choices, and sobering life lessons.

COVID-19 and Clubhouse

Fast-forward to 2021. COVID-19 had the world in its loathsome grip and I, like many others, sought human interaction online. That was when I discovered the Clubhouse app. Through it, I encountered numerous communities, not all of which spoke English, Mandarin, or even Cantonese.

A few of these were Japanese speakers. And some were even offering Japanese lessons. I dove right in, relearning all the basics I had forgotten. Many times, I would simply listen in, try to make sense of the conversation, and Google words that came up. Then I started engaging, hesitantly, in the conversations. I even participated in Mandarin-Japanese exchanges!

Little cultural exchanges.

It was slow going. I wrote an app to facilitate relearning Hiragana and Katakana. I practiced writing every day, religiously. But there was this feeling that I could do more.

That was when someone suggested that I take the JLPT. And from that day forth, I took things up a notch.

Training in earnest

My next step was to install Duolingo on my phone. For the next few years, I faithfully partook of the exercises daily. At the same time, I went onto YouTube to search for JLPT Listening exercises. I didn't really have a sense of how far I had progressed; I just knew I needed to keep going. I've never been brilliant; but what I am good at is being consistent.

One year later, I downloaded the Migii app, and paid the subscription fees for accessing sample JLPT exam questions. With Duolingo, the returns were diminishing. I needed to train a different set of linguistic muscles - the kind used for passing the exam. Duolingo had brought me to a certain point where I could read the Japanese exam questions without too much difficulty... now I had to practice answering them.

The next milestone was hit another year later when, while watching a Japanese TV show, with English subtitles, I noticed something odd. Almost constantly, the subtitles would read "It's OK". Even though "It's OK" was a perfectly reasonable thing to say in the context of the current point of the story, it was a pretty poor translation.

Phrase Literal Meaning English Translation
daijobu It's OK. "It's OK."
dou itashimashite You're welcome. "It's OK."
shinpai suna Don't worry. "It's OK."
anshin shite Relax. "It's OK."
ki ni shinai de Pay it no mind. "It's OK."


At this point, I found myself thinking - wow, these translators are so goddamn lazy! They just use "It's OK" for everything!

And my next thought was - hey, if I know this much, shouldn't I be taking the JLPT already?!

The JLPT Test Voucher

That was when I registered for the JLPT on the Japanese Cultural Society of Singapore website. and paid a hundred Singapore dollars for the privilege. A month later, the JLPT test voucher arrived in the mail. The train was in motion; there was no going back.

Meanwhile, I continued training religiously. With Listening practice, especially. Online chatter had it that the recordings would be played only once, so I trained myself similarly, by not rewinding and replaying.

The JLPT N5 Exam

It was July when the day arrived. I head for the Singapore Management Institute where the exam was held, and stood in line with what looked like mostly Burmese folks. Interesting.

The invigilator who took charge of the exam room I had been assigned to, seemed to be Japanese going by the way she spoke English.

Shading answers with a pencil.

The proceedings were charmingly old-school. We were given question and answer sheets. It was all multiple-choice, and the correct answer had to be shaded with a pencil for feeding into a scanning machine. Despite my numerous certificates, I hadn't done this since... 2015? This was because my last couple certifications were earned from doing the coursework and presentations, rather than standardized tests.

The toughest moment in the exam came from the Choukai, which was the Listening portion. Despite my best efforts, my concentration slipped at various points. It was with considerable relief that I handed in my question and answer sheets, and headed off.

In August, I logged on to the JLPT portal to check my results. I had passed, and my results were more than decent. I actually scored higher on Listening than other sections!

The real value of all this

Last September, I got my actual physical certification through the mail. It was a foregone conclusion by that time, but I still felt that little thrill of pride. Job well done, bro, I told my reflection. Not such a big deal in the larger picture, but we've got to celebrate our wins even if they're small. Especially if said win took thirty-three friggin' years to achieve.

My results.

The JLPT N5 Certification isn't going to change my life. As far as professional cred goes, it's barely a blip. Achieving the JLPT N5 Certification probably puts me on par with the average Japanese toddler where the language is concerned. As for the value of understanding multiple languages? That's not much of a flex. This is Southeast Asia; just about everyone and their dog is multilingual.

No; at my age, the act of learning is arguably more important than what's being learned. It helps stave off dementia.

The real value is knowing and affirming, that with sufficient motivation and putting in the time and effort, I can learn pretty much whatever I choose to learn. And that is powerful stuff. In an age where things are constantly and rapidly evolving, the ability to learn shit has become vital - not just in the tech industry, but for life itself.

In hindsight, I should have realized this. How did I learn ASP? PHP? Ruby? VueJS? D3? All by picking up a book, watching videos, visiting websites, and constant practice. Most of the time, it really is that simple. The methods vary, but at the end of the day, it's about the willingness to put in the hours.

The linguistic journey continues!

I do want to see if I can achieve the next rung, the JLPT N4 Certification. Again, I'm not really sure why. Just for the hell of it, I guess. It's not like I plan to visit Japan. It's not like I realistically have anything to prove to anyone.

On the other hand, Korean does seem pretty interesting.

Decisions, decisions...

Ganbarimashoo!
T___T

Sunday, 12 October 2025

TeochewThunder: Year Eleven (Part 2/2)

Things don't look that good from a viewership standpoint this year compared to last year, but then, they rarely do. I've noticed this for years now. I look at the numbers for this current year and shake my head, only to realize that these numbers tend to double after the passing of another year.

The end result is that the current year's viewership always looks inferior to the previous year's. It's not necessarily the case. Just needs time to settle into the internet.

That said, let's get into the weeds of what apparent successes there are.

Huge hits

I talked about COVID-19, didn't I? And apparently, it hit a chord. The Dark Years of COVID-19: A Software Developer's Perspective was the undisputed winner, hands down.

Do techies lean Liberal or Conservative? was the culmination of a few weeks of frustration as I watched Social Media go mad over the movie Superman and Sydney Sweeney's jeans. And also some rumination I've had over DEI.

What great genes jeans!

Full-time Pay, Part-time Job was something I wrote on the spur of the moment, after witnessing Jeremy Tan's epic speech during the eve of the Polling Day 2025. It inspired me to pen this down, and if I'm being honest, it's not one of my more thought-out works. Still, it doesn't matter; the popularity of the topic and Jeremy Tan, carried the day for this blogpost.

Replit Goes Rogue was a recent addition, but its trajectory is on the rise. Despite being posted less than a month, its numbers are really promising.

OK-ish

So many posts fell into this category. Either huge things were expected for them and they failed by just doing decently, or they punched above their weight.

Why people should (and shouldn't) hire older software developers was just more comparison between younger and older developers.

How much of the Artificial Intelligence hype is just hot air? I suspect this struck a chord with much of the anti-AI brigade, which has been gathering momentum.

What Iswaran's sentence means for those in positions of authority were some thoughts on authority and responsibility. Not so much tech, more workplace-related.

A vacation!

A Software Developer's Vacation in Malacca. A fun piece, with lots of pictures!

Not My Job, Not My Problem is more of a commentary on the workplace, rather than anything tech.

Finally, The Silencing of Charlie Kirk and what it means for Social Media, was written two days after Charlie Kirk was felled by an assassin's bullet. As to be expected, it caught fire fast and it's probably only in this category because of its late inclusion. Some readers called it "balanced". The funny thing is, in the toxic climate that is the USA's Culture Wars, this piece would be vilified by both sides.

Artificial Intelligence Experts join Meta... but it's not about the money? Really? was me responding to more tech news.

Duds

These were the ones that barely raised a whimper. Mostly technical posts which is a tragedy because, well, this is a tech blog. Sorry, not sorry. This is actually in line with the assignment, so I'm gonna keep doing these, regardless.

JavaScript now has negative indexing... sort of was just a report on a new JavaScript function I discovered. It wasn't even that new. And probably I need to work on my presentation because the views suggested that readers found it boring AF.

Is Repeating The Password Field Really Necessary? More of a UI/UX thing. Maybe not the most interesting blogpost in the world, but I think it needed to be written.

Meta Ditches the Fact-checkers - now, I really expected a hell of a lot more out of this one. Either people aren't interested in seeing me shit on Meta, or they just aren't very interested in Meta, period. On the other hand, as mentioned, Artificial Intelligence Experts join Meta... but it's not about the money? Really? did OK, so I really don't know.

Bailing from Meta.

While we're at it, it appears that at this time of writing, in a bizarre twist, A.I experts have left Meta (and all that money) to join some startup. This in no way invalidates my previously implied point that Meta is a deeply problematic company that one would join only if the financial reward was great... the fact that people are not staying in spite of the money, only further reinforces the point.

But this hardly warrants a blogpost all on its own, so... just gonna leave it here.

Thunderation!

Been a pleasure, as always. I love working on this blog, but I also look forward to the annual blogging break. It's where I can get things reset and take stock of the year ahead.

Dialling it up to eleven, yo!
T___T

Friday, 10 October 2025

TeochewThunder: Year Eleven (Part 1/2)

Well, look who turns 11 this year! It's not me (I wish), but it's this blog, of course. This thing here might just be a substitute for the children I'm never planning to have.

Dear God, please no.

In all seriousness though, it occurs to me that the effort taken to maintain this blog and the website has pretty much kept me sane all these years. I read somewhere about journalling with regard to mental health, and it seems that this blog is a great example of journalling. Why's it different from venting on Facebook or X, you might ask?

Well, for one, Social Media posts tend to be a lot shorter and more unfiltered. Which can be a good thing, don't get me wrong, but not necessarily so if you want a more thorough internal audit. Blog posts go through several revisions, as we examine what's going on in our heads, and why, and maybe even how it pertains to the tech space. The final result is a more measured, more self-examined output into the stratosphere. As such, I consider my blogposts of higher quality than a simple vomiting of my initial reactions on Social Media platforms.

That isn't to say I haven't said stupid shit in the past. I absolutely have. But the beauty of time is that as the years go by, I can evolve into less of s shit-talker and more of a shit-thinker. Yikes, that didn't sound better, did it?

Dedication

Also, this is a blog I'm dedicated to.

Dedication is a measure of how consistent you're willing to be in your efforts even without applause or acknowledgement. It's a measure of how much of a shit I give. And I give a lot.

Think about it. In previous years, I could at least justify the effort by the way prospective employers would look at my entire online portfolio. These days, they don't do that anymore (also, I haven't been looking in a while) because even the demos I put out are kids' stuff. I like to think some of it is really well-done, but well-done or not, it's still kids' stuff. Those are just not the things people hire senior developers for, especially not in the age of Artificial Intelligence and Vibe Coding.

So no... there are no longer practical reasons for maintaining this effort. I do these things because I like doing these things.

That's not to say I don't occasionally benefit from a break. And October is my assigned month for that break. Other than this blogpost, there will be no other visible activity. Emphasis on the word visible.

Invisible hands, invisible effort.

You see, as in most software development, the value is largely in the stuff that users don't see. The optimizations. The security fixes. The fine-tuning in the back. That's not to say there's no value in the stuff that's visible, but sometimes I feel like a lot of that is just to placate laypersons who don't know any better.

That's a controversial statement which we should reserve for another day.

To my original point, there is going to be work done. Just not visible work. Mostly prep for year-end, and 2026.

Content

As with last year, I've been making an effort to use less profanities in my writing. Not because I necessarily think the odd (or even frequent) vulgarity is a bad thing, mind you. More because I don't want to develop an over-reliance on anything, not even swearing. I don't want to have to use foul language as a crutch to express myself. It's just poor form. To that end, I am limiting myself to using it only a few times a year in this blog, usually whenever I review a Black Mirror episode. I certainly won't be using them with the same frequency during, say, 2019 to 2022, around the COVID-19 pandemic.

Speaking of which, as the horrors of the past few years fade behind us, I'll hopefully be speaking less about COVID-19 from this year forth. It was a terrible few years, and my emotion-laden rants during that period are evidence of that, but it's time to move on.

You may have noticed that the posts are getting even shorter than they used to. This is not an accident; rather it is the natural evolution of this blog. I wasn't verbally verbose before (at least I hope not) but reading other blogposts and tuning out halfway has made me realize that the lack of attention span on the internet is a very real thing. As a result, I'm going to curb any impulse I may have, to belabor whatever points I may be making.

What else? Yeah I changed the TeochewThunder logo. Talked about that already, didn't I? Hope you like it. If you don't, too fucking bad, baby. It's staying.

Surprise!

This is a tech blog, so I talked a whole lot about tech this year, as always. In particular, I talked about Artificial Intelligence. I suspect that this will be happening with alarming regularity, especially with the frequency with which laypersons feel the need to chime in. Someone's got to show 'em their place! Just kidding... kinda.

As for web tutorials, there's been a nice mix that includes NodeJS and NextJS. and D3. Along with the almost obligatory HTML, CSS, JavaScript sprinkled with the occasional PHP, of course. I started learning NodeJS, as usual, for the heck of it. It increased my understanding of what I was doing with ReactJS and NextJS, so there was value in it.

I've continued to generate images from A.I, but the pendulum has swung back somewhat and once again I've begun to see value in using stock photos.

Next

Highs, lows, hits and misses