Recent Blog Posts

Optical Disc Pitfalls

December 7, 2021 by Steven Ng

I was recently doing a little purging to "spark joy", and some of that purging included backups on optical discs (CD and DVD) that go back more than twenty years.

I'm a little ashamed to admit that I am a bit of a data hoarder, but I'm getting better.

My general feeling now is "if you haven't looked at this in 10 years, you're probably not going to miss it." In any case, I separated my optical disks between data and non-data (e.g., very old software installers, etc.) and still decided to review the content on the optical discs just in case there were some things I'd want to retain.

The one hard lesson that I learned was that not all opticals survived the test of time. The non-surviving opticals saved me a lot of time in terms of review—sort of, but not when you consider how slow optical discs are to read— but they did cause me to rethink my use of opticals as a backup and archive medium.

To be fair, the cheaper the optical discs that were used, the higher the likelihood they were to fail. Having said that, many branded opticals had failed too. Some discs worked on some drives, but failed on others (I was using two different drives on two different computers to try to get through them faster).

But an even bigger problem, is that opticals have, in a way, become a bit of a dinosaur. Unless you have a wallet teeming with cash, tape drives are priced beyond the reach of most small businesses and consumers. Blu-Ray writables provide a somewhat affordable method of cold storage, but my experience with them have been hit or miss.

Another conundrum? How do you destroy an optical disc before disposal? There are many shredders that can shred a CD, but it is a bloody mess. You will end up with tiny shards of sharp plastic and glitter flying all over the place. I discovered that wrapping an optical disc with saran wrap reduces the flying crap produced by the shredding process. Also, some of my older discs (namely Maxell CD-Rs) would jam my shredder.

There used to be a device that would emboss the readable surface of a disk, but they're no longer in production and hard to find. I guess you could use a sander, but the dust particles produced by that probably isn't great for your lungs.

If you have a lot of discs to destroy, I would suggest buying a cheap, secondary shredder (to avoid ruining your paper shredder) and a lot of Saran Wrap.

It also sucks that where I live, there's no way to recycle old writable optical discs. They're basically destined for landfill.

Going back to the offline backup issue, however, what is a person to do? Well, there are cloud services. Using something like OneDrive, Dropbox or something similar is a good way to back up, since they tend to be a relatively frictionless way to get your data in the cloud (and offer a little protection from ransomware attacks). External hard drives and SSDs are good, but you have to remember to disconnect them from your devices, lest they be exposed to a malicious attack or catastrophic user error.

I still wish there was an easy and affordable solution for offline write-once-read-many high capacity removable media backup, but alas, there's simply not enough demand to make that happen.

Housekeeping: Site Updates

November 25, 2021 by Steven Ng

Just some housekeeping notes related to the site that are neither here nor there.

I've moved the site off Github Pages recently, as I did a very minor rewrite of the site, and changed it from a static site to one that requires an actual server. As part of the site refresh, I have finally made the site responsive as it relates to phone screens. It's not as though the site was not readable on small screens before, but it is much better now.

I also recently dropped Google Analytics from the site as well. I never really looked at the stats, and I realized that I'm not that interested in maximizing my audience. I know my audience is microscopic, and overanalyzing what my SEO options are is frankly not a good use of my time. I'm not saying I won't revisit this in the future, but right now, analytics are off the table.

What this means is that on the main domain ( and, no cookies are dropped into your browser. If I have embedded a video from YouTube, you might end up with some cookies for that.

While I currently don't have any applications running on any subdomains (e.g.,, some of those applications may use cookies, but each of those applications will have their own privacy and cookie policy as required.

Because they are subdomains, you will see cookies under the * in your browser.

Also, I have updated content in the Articles section, including adding one for Svelte/SvelteKit resources.

Now that I have a content management system set up, I'm hoping it will result in more frequent posts, but I'm not making any promises there. Better to manage your expectations, right?

My Dockerfile for SvelteKit

November 24, 2021 by Steven Ng

I pretty much do most of my web development in Sveltekit these days. Because I mostly prefer to deploy to Docker containers, it comes in handy having a reusable Dockerfile template.

I've got a template below that can be used with Sveltekit applications built with the node-adapter.

FROM node:16-alpine

ENV NODE_ENV production

RUN apk add dumb-init
RUN apk update && apk add bash

WORKDIR /home/app

COPY package*.json ./
COPY .env ./

RUN npm ci --only=production

COPY . .


CMD ["dumb-init", "node", "/home/app/build/index.js"]

The key points to know:

  1. I use /home/app as my working folder, feel free to substitute that with your own preferred path.
  2. I don't use my Sveltekit .env for storing environment variables. For me, it's more like a config file for my web application. My .env file contains the names of the environment variables that I use in the app, which is why I copy it to the container.To access environment variables in my Docker compose file, I access them using process.env.
  3. I like to have a bash shell in my containers in case something bad happens.
  4. I use dumb-init to start my process as PID 1.
  5. I copy . . because I have additional files beyond the build folder as part of my application (for database migrations, etc.). I use .dockerignore to exclude anything not required in my container image.

Cognos Bug of the Day

February 4, 2021 by Steven Ng

Experienced a nice juicy user interface bug in the Administration Console today. It's pretty obscure and specific, but knowing about it will prevent you from chasing down the wrong paths in terms of root cause analysis.

This little UI issue is a sanity breaker though, as it will have you second guessing what you did.

Standard checkbox behaviour in a web browser is that clicking on the label for a checkbox will toggle its state from checked to unchecked and vice versa. Since the label is usually a much bigger target to click on than the checkbox, you might prefer to click on the label over the checkbox itself.

In the Cognos Administration console, if you click on a checkbox's label to disable an entry, it works perfectly fine. But if you click on the checkbox's label to enable an item, it doesn't save the change. Note that the "Hide this entry" checkbox value saves fine, so it is isolated to the "Disable this entry" checkbox label.

Cognos Properties Page

Your first thought is likely to be "Well, this feature has been around forever, they've tested all edge cases. It can't be a UI bug, since I did the same thing to enable it and it worked." You might be thinking maybe it's a permissions or capabilities issue. Surprise, surprise, it is indeed a user interface bug (reproducible in 11.1.7 and 11.1.4).

So let me save you a bit of time hunting this issue down, because searching for "Cognos 11 can't enable printer" or whichever item you're trying to enable probably won't give you a very good result (unless this blog post shows up, haha).

Lucky for us, there is a workaround. You can re-enable the item by clicking on the checkbox itself and then saving.

I'm not in the habit of figuring out someone else's bugs, but curiosity got the best of me, so I looked at the markup in my browser's developer tools. If you're not into coding, you'll probably want to skip the next bit and be on your merry way.

It looks like the checkbox is inside a table, and when you click outside of the checkbox, the <table> tag has an onclick event that changes state of the checkbox (it wasn't the label after all). The problem is that the value is tracked not in the checkbox control, but in a hidden text field (which is storing the value being saved to the content store), and that field isn't changed to false when the "Disable this entry" checkbox is checked (it is for the "Hide this Entry" table click though).

I'm sure the markup is quite old (the heavy use of tables is a good indicator), as backwards compatibility with Internet Explorer is still a thing in the enterprise world, even today. Having said that, by modern web coding standards, the amount of code being used to manage a boolean state on a checkbox is absolutely bonkers.

I Made a Viewer for cognosserver.log Files

December 11, 2020 by Steven Ng

I had to go through a cognosserver.log file recently, and boy was it painful.

There is so much work to separate the wheat from the chaff, and looking at the log file even in a good text editor can make any person's head explode.

I've made a static web application (here: that lets you either load a cognosserver.log file, or paste a snippet from a log file into it.

Once you've loaded the log in, it shows you a cleaner view of the log file, and you can filter by keyword, or flag log entries to make filtering even easier.

Note that this app only reads cognosserver.log files. At some point I may make a separate app that reads audit logs, but I was focused at solving the pain point I had at the moment.

In terms of data storage, none of the log files you load are stored on any server. I use the browser's built in data store (works similar to a cookie), and I've provided a mechanism for you to wipe the database.

In terms of performance, since everything is running in a browser, the bigger the file, the slower it will be to load and process. Also, I've only tested it with Cognos 11.1.x logs. No guarantees it will work on older logs.

This app wasn't written to be sold or anything, so I only did a minimal amount of testing and trapping. Keep the old adage of "garbage in, garbage out" in mind when using it.

You can report issues here: but there's no guarantee I'll address them, as this was only designed for my specific needs and not be some huge do-it-all application.


My SARS-CoV-2/COVID-10 Links Article is Up

March 20, 2020 by Steven Ng

The mass spread of the SARS-CoV-2 virus in March 2020 effectively changed the world. We are seeing reactions at both extremes - panic and dismissal. Personally, I think a huge part of this is related to communication. The traditional news and media organizations have done a satisfactory job of reporting events, but not as good a job at educating people on just about everything.

Fortunately, many individuals have stepped up and provided better informative sources than the ones you'll get by watching the news or reading newspapers.

I have posted an article with links that I consider to be of high quality as it relates to the pandemic.

As I find new quality content, I'll add it to that article.

Let's be safe everyone, we will get through this.

Minor Site Update

March 19, 2020 by Steven Ng

Just a minor update to the site. As part of my "continuous improvement" process, I've made the site more mobile friendly. It's not that the first iteration wasn't viewable on a phone, but the top navigation reflows to different screen sizes more cleanly than in the past.

There are some minor UI changes, as the Search box has been reskinned.

I've integrated my "standard" Svelte stylesheet into the site. Gotta eat your own dogfood, as they say.

My Curated List of Free Fonts is Up

March 12, 2020 by Steven Ng

An important part of any web developer/report designers tool chest is a good set of fonts.

I've posted a new article entitled My Favorite Free Fonts in the Articles section of my site.

Not everyone (myself included) can afford to license a typeface like Avenir Next, Proxima Nova or Gotham, so I've compiled a set of go-to fonts that I use in my projects.

The article is organized by download site and then categorized by type.

Of the list, my favorite fonts are:

I'll be updating that article periodically, as I discover new favorites, or if my tastes change.

It's 2020 and I'm Still Using Coffeescript

March 10, 2020 by Steven Ng

Get Off My Lawn

I find that technical choices can be an especially polarizing topic among techies, especially with the newer generation.

While tech has never been immune from the "smartest guy in the room" syndrome, programming language debates can often be fatiguing to watch, and soul sucking to engage in.

So here's my old geezer "you don't know how good you have it" lecture. When I was coming up in the tech industry three decades ago, there were only a handful of programming languages you could get gainful employment in. It was usually a some variant of C, a mainframe language, Java, or for a time Visual Basic. Everything else was dismissed by the smartest guys in the room as a "toy language".

Today, you can get gainful employment in a wide variety of languages. In addition to the old standbys, Python, Ruby, Javascript, Typescript, Erlang, Go, Swift and a pile of other languages are pervasive and popular. I think it's great that there are so many acceptable language options today.

If you've been watching the Javascript world, you'll probably notice that everyone in the know has jumped onto the Typescript bandwagon over the past few years.

I don't have any quibbles with Typescript, but I haven't jumped on that bandwagon... yet. I'm still using Coffeescript. If you're not familiar with Coffeescript, it's a language written in Javascript that compiles into Javascript. Wait, what?


This description will date me, but Coffeescript is essentially Javascript shorthand. It lets you to write Javascript with a Ruby-ish/Python-ish syntax. Coffeescript is sweetened by a pack of syntactic sugar resulting from the elimination of semicolons and braces as well as the improved readability of nested logic using indented whitespace.

For example, this Coffeescript snippet:

palette =
  "gray" : ["#dee2e6","#ced4da","#adb5bd","#868e96","#495057","#343a40","#212529"]
  "purple" : ["#e5dbff","#d0bfff","#b197fc","#9775fa","#845ef7","#7048e8","#5f3dc4"]
  "blue" : ["#dbe4ff","#bac8ff","#91a7ff","#5c7cfa","#4c6ef5","#3b5bdb","#364fc7"]
  "green" : ["#c3fae8","#63e6be","#20c997","#12b886","#0ca678","#099268","#087f5b"]
  "yellow" : ["#fff3bf","#ffec99","#ffe066","#ffd43b","#fcc419","#fab005","#f59f00"]
  "orange" : ["#ffe8cc","#ffc078","#ffa94d","#fd7e14","#f76707","#e8590c","#d9480f"]
  "red" : ["#ffe3e3","#ffa8a8","#ff8787","#fa5252","#f03e3e","#e03131","#c92a2a"]

colors = Object.keys(palette)

shades = ["lightest","lighter","light", "", "dark", "darker", "darkest"]

foreground = (shade,background)->
  color = "#ffffff"
  switch shade
    when "lightest","lighter","light"
      color = "#495057"
    when ""
      if background == "yellow" then color = "#495057"
    when "dark"
      if background == "yellow" then color = "#495057"

Compiles to this:

var colors, foreground, palette, shades;

palette = {
  "gray": ["#dee2e6", "#ced4da", "#adb5bd", "#868e96", "#495057", "#343a40", "#212529"],
  "purple": ["#e5dbff", "#d0bfff", "#b197fc", "#9775fa", "#845ef7", "#7048e8", "#5f3dc4"],
  "blue": ["#dbe4ff", "#bac8ff", "#91a7ff", "#5c7cfa", "#4c6ef5", "#3b5bdb", "#364fc7"],
  "green": ["#c3fae8", "#63e6be", "#20c997", "#12b886", "#0ca678", "#099268", "#087f5b"],
  "yellow": ["#fff3bf", "#ffec99", "#ffe066", "#ffd43b", "#fcc419", "#fab005", "#f59f00"],
  "orange": ["#ffe8cc", "#ffc078", "#ffa94d", "#fd7e14", "#f76707", "#e8590c", "#d9480f"],
  "red": ["#ffe3e3", "#ffa8a8", "#ff8787", "#fa5252", "#f03e3e", "#e03131", "#c92a2a"]

colors = Object.keys(palette);

shades = ["lightest", "lighter", "light", "", "dark", "darker", "darkest"];

foreground = function(shade, background) {
  var color;
  color = "#ffffff";
  switch (shade) {
    case "lightest":
    case "lighter":
    case "light":
      color = "#495057";
    case "":
      if (background === "yellow") {
        color = "#495057";
    case "dark":
      if (background === "yellow") {
        color = "#495057";
  return color;

You'll notice that the Coffeescript code is terser, which in my opinion, makes it more readable.

Coffeescript, however, does not absolve you of needing to know any Javascript, as debugging still requires full Javascript fluency. As I mentioned, Coffeescript is shorthand for Javascript, but not a replacement.


So why do I still use Coffeescript, even though it's 2020 and there are arguably better (and inarguably more popular) options?

Well, I started using Coffeescript when I started to do Ruby on Rails development roughly 10 years ago. If you've spent any time doing RoR, you'll know that indented syntax languages like SASS and Haml are incredibly popular in that world. SASS and Haml let you write CSS and HTML without becoming muddled with braces, semicolons or open and close tags. With Rails 3.1, Coffeescript adoption became even more widespread among RoR devs.

Using Coffeescript with Rails has real benefits, as Coffeescript code has a lot of similarities to Ruby, which means less mental "mode switching" when jumping back and forth between front-end and back-end code, which makes it feel like you're writing in the same language across the breadth of your entire application.

So back to the question of "why", I still use Coffeescript today because it allows me to translates my thoughts into code faster without worrying about details like braces and semicolons. The use of whitespace also makes the code incredibly easy to read for me, which makes debugging much faster. The primary cost of this language is that it needs to be compiled (which for me happens in near real time, since I use a Gulp script to compile my code when files change. Now that I'm working with Svelte, which is a compiled web development platform, the notion of Coffeescript's compilation step as an inconvenience is moot, as Svelte requires compilation anyways. I simply include Coffeescript as a seamless preprocessing step in my Svelte workflow.

Looking Forward

I have been tempted to switch to Javascript (or even Typescript) now that I've been using Svelte, mainly because of some Coffeescript-unfriendly idiosyncracies in Svelte (i.e., export let, let and $: { someCode}). Those idiosyncracies, however, are easily solved with the use of backticks, which in Coffescript, lets you pass through raw Javascript in your Coffeescript code.

So while my use of Coffeescript might be considered passé to some, I still find it to be a valuable part of my toolchain, because I am more efficient and productive with it. Having said that, I'm not at all ignorant or resistant to change. Myself, I'm partial to tools that work the way I think, and sometimes timing matters. Industry-wide preferences often become replaced with even newer ones.

For example, I've been looking to move away from AngularJS for newer web projects for some time. I considered Angular (AngularJS's successor whose name is not at all confusing, smirk), React and Vue. I was about to jump on the Vue bandwagon until I heard about the Svelte 3 announcement, and was simply blown away by how it ticked all the boxes that I was looking for in an AngularJS replacement. Had I made my choice earlier, I'd be a Vue developer right now. Timing affects outcomes.

So yes, I could adopt Typescript now, but I need some convincing that at some point Typescript won't fall out of favor of something newer, faster and better. In the end, whether you're using Coffeescript or Typescript, you're still compiling that language to plain old Javascript, since that's what runs on browsers and NodeJS. So why not stick to the tool that works best for you, whatever that may be?