Usability - Productivity - Business - The web - Singapore & Twins

Develop your SPA with vite

You drank the SPA coolaid to develop with KEEP. While you can use the usual suspects, most cases Vanilla JS will do fine: one each of index.html, index.css and index.js

The preview problem

Since the files are static, throw them on the server a you are good - of course your regular operation gets disrupted. Throw them on a preview server and your calls to /api/... will fail. You could hack around by providing full URLs, you just enter CORS hell then.

viteJS to the rescue

viteJS brands itself as "Next Generation Frontend Tooling" with the catchy tagline "Get ready for a development environment that can finally catch up with you". Let's give it a spin:

npm create vite@latest

The result is simple

Vite start

The package.json lists no runtime dependencies and you can run npm run dev to preview the sample page.

Adding the proxy

When starting vite, it looks for vite.config.js for settings. There you can specify all needed proxy settings.

import { defineConfig } from 'vite';

// https://vitejs.dev/config/
export default defineConfig({
  server: {
    proxy: {
      '/api': 'http://localhost:8880',
      '/.well-known': 'http://localhost:8880'

The vite.config.js allows for sophisticated configuration like conditional settings (think testing against dev, staging, production), which is up to you to evaluate.

Using npm run build vite works its magic to build a combined distributable app, SPA or otherwise.

As ususal YMMV

Posted by on 25 April 2023 | Comments (0) | categories: Software WebDevelopment

Tabs vs Spaces - Mozilla changes sides

In a surprise move the Mozilla Foundation announced to join team spaces in the ongoing developer dispute Tabs vs. Spaces.

Rationale and Value proposition

In a recent statement Mozilla confirmed: "_We are seeing the errors of our way being team tabs for so long_". Mozilla sees an number of advantages in the switch from tabs to spaces:

  • Differentiation: IE, Edge, Chrome, Safari, Brave and all the other browsers will look dated compared to the spaces based Firefox
  • Freedom from patent US20070136665A1
  • Value delivery: a tab only delivers a paltry HEX09, while a single space delivers already a HEX20 and gets delivered two to four times in the same visual space
  • Upgradable: HEX20 can be upgraded to HEXA0 with no optical change (OK, your YAML will break, but it will break anyway)


Mozilla will rollout "Firefox Spaces" in phases:

  • In the first interation warnings will be generated if a site you visit uses tabs. The warnings are on the console only
  • Phase 2 will feature warning banner overlays
  • Phase 3 will see the replacment of tab navigation by "Firefox in Space"
  • Finally sites using tabs will be blocked and reported to NIST's Cyber division

As usual YMMV

Posted by on 01 April 2023 | Comments (0) | categories: Software

KPI measurements and feedback loops

Commonly attributed to Peter Drucker a cornerstone of management is "If you can’t measure it, you can’t manage it." (which according to the Drucker Institute he never said).

It's a Damned anyhow situation. In my oberservation KPIs are plagued by linear thinking and, what I call Gamability. Let me explain

Meet the feedback loop

We are caught in a "newtonish" world of belief that there is a linear relationship between a cause and effect. Most notably visible in post mortem demand for a root cause analysis (At least science gives ceteris paribus a thougth).

In his book The Fifth Discipline the author Peter M. Senge offers a different view and model. Most system, from small atomic particles to the Universe are bound in a series of feedback loops. The emphasis here is on loops -> plural. The various execution speeds lead to observable results, that can't be explained with single linear thinking.

Let's take a sweet example, that should be easy to relate:

Chololate eating and happiness

You don't feel happy, so you eat your favorite food. Now your happy. Two (obviously simplified) feedback loops kick off. The red lines indicate that the loops work with a delay.

The upper one removes the "favorite" from the food, while the lower one removes the hapiness inducing effect. Pretty obvious and as I said grossly simplified.

Read more

Posted by on 30 March 2023 | Comments (0) | categories: Business Learning

POPIAH - an agile journey in 8 iterations

In a recent chat a store manager shared how tedious the annual physical stock taking is, since the company doesn't provide handheld barcode scanners. Why not use your phone I replied. So after a short discussion it was agreed to build a MVP.

The geek corner of my brain went into overdrive: databases, authentication, protocols, libraries - the whole shebang. If you don't reign into that, you end up with the famous "swing design"

Swing design, the customer wanted a tire

(shamelessly borrowed from here)

Luckily there's the agile way: add value incrementally. Our increment frequency was one improvement per 15min, tiny step by step ;-).

The result is POPIAH: Poor Operator's Personal Inventory Assessment Helper (not that one). The final (for now - pending making it pretty) result looks like this:

Application screenshot

Step by step

Our epics (defined looking back) were:

  • make scanning work
  • make the screen useful
  • allow using the data elsewhere

Read more

Posted by on 04 March 2023 | Comments (0) | categories: Agile Development

TOTP and vert.x

Time-based one-time passwords (TOTP) are a common security feature in Identity Providers (IdP). There are use cases beyond IdP, mine was "Understanding what it takes").

TOTP interaction

You have two phases: enrollment and use. During enrollment a secret is generated and (typically) presented as QR Code. A user points one of the many Authenticator apps to it and gets a numeric code that changes once a minute.

When you use it, you pick the current number and paste it into the provided field. The backend validates the correctness with some time leeway.

What it is not

Typically when enrolling you also get recovery codes, sometimes called scratch codes. They are NOT part of TOTP and implementation is site specific and not standardized. An implementer might choose to check your recovery codes when your TOTP fails or provide a separate interaction using those.

The initial confirmation, is actually the first instance of "use" and one could have a successful enrollment without it. This is depending on the implementation.

It isn't foolproof. An attacker could trick you into typing your TOTP code into a spoofed form or just hijack your session (cookie). That's why responsible web apps run a tight security with CSP and TLS (and once browser support is better Permission Policy)

Setting up a sample application

We need a landing page and its supporting files (css, js, png) served statically and 3 routes:

  • request
  • save
  • verify

Read more

Posted by on 07 February 2023 | Comments (0) | categories: Java vert.x

learning how nginx proxy works

A common approach for web applications is to serve them behind a reverse proxy. My favorite there is nginx. It has a fairly understandable configuration, supports http/2 and is fully supported by Letsencrypt's certbot.

proxy_pass can be tricky

The configuration for reverse proxying is a combination of location and a proxy_pass statement, as well as some headers. In it simplest form the URL path and the proxy_path URL are the same, so you don't need a translation between direct access (e.g. local testing) and access through nginx. A configuration could look like this:

location /someurl {
        proxy_http_version 1.1;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

The headers ensure your application knows the original request source and allow upgrade from http to websockets.

It gets interesting when location and proxy_pass don't match. Differences can range from different url path to regex and rewrites. Especially when inheriting (or googling) configurations, understanding can be a challenge.

Express to the rescue

A few lines of expressjs will echo back what ends up at the backend. Together with curl it provides you a practical learning and testing environment.

const express = require('express');
const app = express();
const port = 3010;

app.get('*', (req, res) => {
  res.setHeader('Content-Type', 'text/json');
  let result = JSON.stringify(
      headers: req.headers,
      url: req.url,
      query: req.query,
      hostname: req.hostname

app.listen(port, () => {
  console.log(`Echo app listening for GET on port ${port}`);

Enjoy, as usual YMMV!

Posted by on 09 January 2023 | Comments (0) | categories: nginx WebDevelopment

Java style guide: Functions, Streams and Optionals

Over the years I developed a certain style developing in Java (and other languages) that made my code more readable. This isn't about the formatting style guide (Just stick to Google) or making code spotless, but about style.


Code needs to be testable, easy to adopt and understandable when someone else, like your future self, tries to read it. Starting with SOLID principles, the guidelines help to get there. They work for any style of development, while I strongly recommend looking at TDD, Dave agrees, so does Allen. In no particular order, let's dig in.

Read more

Posted by on 08 January 2023 | Comments (0) | categories: Development Java

Please wait until that HTTP service is ready

Our brave new world of containers and microservices runs on a combination of YAML and shell scripts.
Getting them to run in the desired sequence can be a challenge.

When ready isn't ready

All container environments have a "depends" clause, so the container runtime can determine the correct startup sequence for the zoo of containers comprising the application to be launched. Each container will usually signal when it is ready.

However ready can mean different things to different applications.

In the container world it should be: the service is available.
However it could be: service was successfully started, but might be busy with house keeping. In the later case the start scripts of the dependent services need to do their own waiting

curl to the rescue

Curl is the Swiss Army Knive of network tools. It probably takes a lifetime to understand all its possibilities. Make sure you have the current version, but at least 7.52.0 from 2016.

Whipping up a little shell script provides what we need:

# Wait for a http service to be available
export TARGET=http://baseservice.local/
curl -v \
     --retry 10 \
     --retry-delay 20 \
     --retry-connrefused \

if [ "$?" -ne 0 ]; then
  echo "Failed to reach $TARGET"
  exit 1
  echo "$TARGET has replied!"

Let's break it down:

  • export TARGET defines the URL you want to reach. Works for both http and https. Potentially works for other protocols supported by curl as well, but I haven't tested that (yet). You might swap it out for $1 to capture the first parameter called
  • \ denominates line continuation. The whole command needs to be on one line which is hard to read. \ tells Linux to treat the following line as belonging to the current one. On Windows you need to replace \ with ^ and alter the syntax according to Powershell
  • curl -v Verbose output, so the logs tell the full story. When you work with privat CA or self signed certificates (Don't do self signed, use your own CA), you need to add -k or --insecure to make this work
  • --retry 10 try the connection 10 times
  • --retry-delay 20 Wait 20 sec until the next try
  • --retry-connrefused retry even on "connection refused" responses. By default curl retries only http responses not in the 2xx range. Connection refused is HTTP's way telling you: there's nothing taking your call, which curl, without --retry-connrefused takes as final, fatal error and terminates retries. When you have curl v7.71.0 (from 2020 or later), you can use --retry-all-errors instead. The documentation warns This option is the "sledgehammer" of retrying. Use it wisely
  • if [ "$?" -ne 0 ]; then Commands usually return a numeric result. Anything other than 0 indicates a failure condition of some sort. When curl has exhausted the number of retries a value greater 0 will be returned. To capture the return value we us $?
  • return 1 We indicate that our script failed. So it can be called from other scripts

We could swap out $TARGET for $1 which makes the script callable handing over the target URL e.g waitforme.sh http://someURL

Beyond containers

The script is also useful in "classic" situations, where you start a service in the background (which by definition is asynchonous). Your local environment uses anything like:

  • ./startdb &
  • systemctl start myservice
  • domino[_container] start

You can use the curl retry for any automation. Adjust the wait time and the retry time to your needs.

As usual YMMV

Posted by on 02 January 2023 | Comments (0) | categories: Container Development K8S

On Technical Interviews

When the HCL Labs is hiring engineers, we conduct technical interviews. I had and have the priviledge to interview, hire and work with incredible talented individuals in the process. Lets look behind the curtain of the process.

What we are looking for

The technical interview is conducted by the interviewie's potential future colleagues. We try to gauge skills, conduct and team fit. Fit includes: does it improve diversity. You got a tatoo, tell me how you picked the design? You like tailored suits, who's your tailor? Punk is your thing, what's your favorite band? - You get the drift.

Can the candidate explain technology to peer engineers and understands the fundamental concepts? We found a surprising number of developers who don't know how their platform actually works. Topics like "The Node event loop" or "What does Maven do".

Knowing your environment is essential. Have you kept uptodate with latest changes, even your current role holds you back on applying them.

Does the candidate know about our "Buzzwords" YAGNI, DRY, KISS, SOLID or pattern

What we ask

Questions come from all areas: general project conducts, APIs, Code (mostly JS, TS & Java), HTTP and databases. We are looking not only for "I have x years experience", but also general awareness.

Example: you might not have a chance to work with a graph database, but you know they exist, what they are supposed to solve. etc.

A little taste:

  • How much Scrum does one entertain to be agile and what's the essence of agile
  • How to interact and resolve ambiguity in tasks and asks
  • On object oriented vs functional programming styles
  • Types of NoSQL databases and their uses
  • How do the joins (left, right, inner, outer) in an RDBMS work
  • Use cases for SQL and NoSQL
  • HTTP Verbs: POST vs PUT vs PATCH
  • REST experience including a tool or another
  • HTTP return codes. Everyone knows 200, some 201 and the difference to 200, and a few 418
  • CORS and CSP
  • Callbacks vs. Promises
  • let vs var vs const
  • function vs =>
  • Loops vs streams
  • Testing: TDD, UnitTests and Integration Tests
  • SQL Injection and prevention
  • Maven & NPM
  • Git & GitHub
  • What do you use NodeJS for

Coding challenges

Coding challenges in interviews are controversial and they are usefull.
We keep ours simple and explicitely allow questions, dialogue or googling for answers, with the warning label: you need to explain your solution, no matter what.

Being a little sneaky we keep the challenge requirements fuzzy to check if the candidate will ask for clarifications.

The exercises are light and not too far from practical use:

  • talk to an API
  • refactor code
  • fix performance

Work with us

HCL in Manila is looking for engineers of all levels and career stages. Come join us, some current openings from Linkedin (links are temporary, grab them while they are hot):

Posted by on 12 December 2022 | Comments (0) | categories: Java JavaScript Software

Async testing with vert.x

Test driven development gets interesting when actions might or might not complete somewhen in the future. Point in case: HTTP requests (fetch returns a promise)

vert.x and jUnit5

I frequently write little tests, that spin up an http sever before the test, run individual tests using a WebClient. All these operations run asynchronous, returning a vert.x Future (In JavaScript Land the equivalent would be a promise)

To make this pain free, vert.x provides a full integration into jUnit 5. Using the annotation @ExtendWith(VertxExtension.class) vert.x provides two injectable parameters: Vertx and VertxTestContext. Add the simple mental rule: All async operations need to interact with the VertxTestContext.

Read more

Posted by on 03 November 2022 | Comments (0) | categories: Java vert.x