Agile is an adjective, not a textbook

Posted on Leave a comment

This is probably the single best advice there is regarding agile software development, offered by the authors of The Pragmatic Programmer under section “The Essence of Agility”. Having worked in agile and pseudo-agile teams myself, I believe this is a valid point conspicuously disguised as a rant. Teams often tend to follow some off-the-shelf agile model as a written-in-stone guidebook, and in doing so inadvertently move away from the very essence of agile.

Just the following four deceptively simple values should apparently put one on a path to true agility:

  1. Individuals and interactions over processes and tools
  2. Working software over comprehensive documentation
  3. Customer collaboration over contract negotiation
  4. Responding to change over following a plan

In my experience, I’ve found #2 and #3 to be the most difficult to comply with as they often involve setting expectations with the client which can be tricky, unpleasant, or downright impossible.

These four values should guide not just project managers and analysts on a team but also developers. The art of creating good software doesn’t only lie in writing clean code but also in enforcing inclusive and structured communication patterns.

Don’t dread the Thread, it’s just Java async code

Posted on Leave a comment

I grew up with a fear of Java Threads. It was a time when Java and C# were the only “serious” programming languages to be used to write enterprise software. This was especially true in India, which to date remains a laggard in terms of technology adoption. The concept of Threads was hard enough for a budding programmer. And to make matters worse, the Thread API was far from being intuitive. Concurrency, deadlocks, semaphores, and synchronization were topics that were thrown around as stone pelters throw stones on anyone approaching their territory.

The concept of async code in other languages seemed equally mind-bending at first. During the Web 2.0 days, creating dynamic interfaces via Ajax was cool but it forced one to understand async code flow in JavaScript. Somehow, JS (or more precisely, jQuery) managed to make async code not as hard to understand as Threads in Java. Yet, at a grassroots level, both did the same thing—allow developers to write non-blocking code.

Reading the chapter on Concurrency in the book The Pragmatic Programmer triggered this comparison in my head. Having worked on professional software for over 12 years in several different languages, async code now feels intuitive. Back then, it was hard. Threads were hard. Not only would I skip reasoning about potential concurrency issues in my software, I would also leave out without flinching any discussion about concurrency or threads in reference material. Surely threads were designed to be used in “very complex” applications, something that I would not be doing until my late thirties. Hah!

It’s only now that I realize concurrency is unavoidable—it’s implicitly baked into so many daily problems that not thinking about concurrency means knowingly or unknowingly creating software that would not scale to survive the harshness of the real world.

So, here I am writing a note to my younger self:

It’s useful to think of threads simply in terms of tools that provide the ability to execute pieces of code asynchronously. Before asyncawait and Future , the only way to run async code in Java was to use a Thread. In short, a thread is just Java async code.

Node.js made it super-easy to run async code via callbacks, so much so that it became a preferred standard when writing I/O heavy code. So, programmers who would not write async or non-blocking code in Java due to the fear of threads could easily do so in Node.

Clean Code

Posted on 3 Comments

There’s a bunch of technical books sitting in my Amazon wishlist for quite some time. Poor books! They could not end up in my shopping cart for a myriad of reasons, laziness to read being chief amongst them.

The importance of reading technical books simply cannot be overstated. Or reading articles, for that matter. Reading is what helps you to improve your craft, keeps you up to speed with the topics you care about, makes you a better version of yourself in your desired skill. Personally, I’ve found books to be more useful than online courses. I have nothing against online learning, I’m all for it. I have end-to-end finished a tonne of courses on Udemy, Pluralsight, Coursera, etc. The things that go for me with books are that I can take them anywhere (even where there’s no Internet), highlight important parts, scribble my own thoughts and most importantly learn without distractions. The thing about books being little portable time machines is true.

Recently when I was casually digging my Twitter timeline, I came across this book called “Clean Code”. Someone had written life-changing praises about it. Out of nowhere, I decided to order. Out I went to Amazon.in, found the book, read a few reviews, noticed that there was no discount, and hit the Buy Now button.

Clean Code is a book about software craftsmanship. It’s a must-read for anyone who cares about their craft.

I am a little more than halfway through the book, and boy, it is life-changing. It’s written in an authoritative tone by a bunch of regarded software engineers with decades of development experience. The experience clearly shows! Chief among the authors of the book is Robert C. Martin, who is one of the founders and foremost popularizers of SOLID principles and Agile methodology. I have thoroughly enjoyed the book so far, and am looking forward to finishing it. It’s safe to say that this book has reignited my craving for technical books. Next up I’ll probably pick up a Martin Fowler.

Using the learned clean code principles, I was able to transform this ugly chunk of code at work in a beautiful verse below it. The code is customization on top of Sitecore‘s JSS Node Proxy open source program.

Which code snippet is easier to read?

Object.keys(tenants).forEach((key) => {
  const tenantName = tenants[key];
  const proxyConfig = config.initConfig(tenantName);

  server.post(
    `/:lang((${routesLangs}))/${tenantName}:paymentRoute((${intermediatePaymentRoutes}))`,
    bodyParser.urlencoded(),
    (req, res) => {
      handlePaymentIntermediateRoute({
        paymentData: req.body,
        path: req.params.paymentRoute,
        lang: req.params.lang || DEFAULT_LANG,
      }).then((svcData) => {
        proxyConfig.serverBundle.renderView(
          (param, html) => {
            res.send(html.html);
          },
          req.path,
          svcData,
          { userAgent: req.headers['user-agent'] }
        );
      });
    }
  );

  server.use(
    `/:lang((${routesLangs}))/${tenantName}([/].*)?`, // eg. /((en|ar))/yasisland/wishlist
    (req, res, next) => {
      if (req.originalUrl.indexOf('/-/media') === -1) {
        let originalUrl = req.originalUrl.replace(tenantName, '');
        originalUrl = originalUrl.replace('//', '/');
        req.originalUrl = originalUrl;
      }
      next();
    },
    scProxy(
      proxyConfig.serverBundle.renderView,
      proxyConfig,
      proxyConfig.serverBundle.parseRouteUrl
    )
  );
});
server.use(
  '*',
  (req, res, next) => {
    setPaymentRoutePreReqs(req, res, next);
  },
  (req, res, next) => {
    const envType = getEnvironmentType(req);
    const tenantName = getTenantName(envType, req);

    const paymentRoute = req.paymentRoute;
    if (paymentRoute) {
      handlePaymentRoute(req, res, tenantName, paymentRoute);
    } else {
      handleApplicationRoute(req, res, next, envType, tenantName);
    }
  }
);

Fixing headphone jack in Ubuntu 20.04

Posted on 7 Comments

As weird and funny as it may sound, Ubuntu isn’t able to play sounds on my laptop‘s 3.5mm headphone jack. Yes, the gentle, innocuous, innocent headphone jack that we all love and use. And mine’s not even a super-fancy laptop with uncommon hardware. It’s a Dell Inspiron 7000 series laptop with an Intel chipset. How more commonplace can things get than this?

After fiddling around for quite a bit, I was finally able to get it to work. I guess the issue has something to do with (the notorious?) PulseAudio.

Anyway, the trick for me was to use a little utility called hdajackretask. It’s part of the alsa-tools-gui package and helps you retask (remap or whatever) your PC’s audio ports (including internal speakers and HDMI). Read the documentation here (simple and fun): https://fossies.org/linux/alsa-tools/hdajackretask/README

Install it using this command:

sudo apt install alsa-tools-gui

Next, open the app from either command line (hdajackretask) or menu. Here’s my overriden configuration for reference (headphone, right side):

Hit Apply Now, and boom! Headphones are finally working. It’s important to note that this is not a silver bullet that will fix all jack-related sound issues. It just so happened in my case that retasking was necessary. I have to do it every time I plug in my headphones (even with the boot override installed). Weirdo, I know, but at least it works.

You may receive this error when you apply the overrides. I guess it’s totally fine to ignore it. It’s probably because of a restart of pulseaudio system. Things work despite the error.

Feels to weird that it’s 2020 and such retasking has to be done manually!

Note that getting my Bluetooth audio devices to work – especially my AirPods – remains a pain in the you-know-what.

How to deal with node_modules in a Dockerized Node application?

Posted on Leave a comment

There are tens of thousands of blogs, articles and forum threads out there on containerizing a Node.js application. We have everything from outright bad advices to informed and tested opinions. This short post is about a very specific aspect of Dockerinzing a Node app, something that is usually not addressed or given as an after-thought in these articles.

Once you have a Node app up and running in Docker, things are well until you – like all good developers – start adding or removing NPM packages. Nasty things happen, such as:

  • packages get installed on your local (host) filesystem but are not available inside the container
  • packages get installed on your container’s filesystem but are not available on your local
  • there’s a mismatch between versions of the same package in host and container
  • permission issues while installing or removing packages

All these can be solved by correctly using Docker volumes. A volume is basically a way to make parts of container’s filesystem available to host and vice-versa. Not understanding volumes properly may also lead to one of the issues listed above.

I had a similar experience in one of my projects. See the fix:
https://github.com/universalnative/un-website/commit/0eaaca98adac46bf9b1553e66d6acd9d731db43d

As noted in the stackoverflow answer cited in the commit:

When docker builds the image, the node_modules directory is created within the worker app directory, and all the dependencies are installed there. Then on runtime the worker app directory from outside docker is mounted into the docker instance (which does not have the installed node_modules), hiding the node_modules you just installed. You can verify this by removing the mounted volume from your docker-compose.yml.

https://stackoverflow.com/a/32785014/1775160

This is a powerful thing to remember. Once you know how volumes work, you’ll be able to better troubleshoot your Node app when things are wrong. For example, when I recently installed a new NPM package in my local I knew I had to do something like this to make it available inside the container as well:

https://github.com/universalnative/un-website/commit/3fa9c9a79c35a3ddbf39079d16b7dbca12105c0d#diff-4e5e90c6228fd48698d074241c2ba760

Why didn’t I just mount my host node_modules to container’s node_modules? Here’s the explanation:

An alternative approach is to mount host node_modules as a volume in container, but that will override container’s own node_modules folder with host’s. Keeping things independent allows for cleaner and easier troubleshooting of installed/missing packages.

This the approach I prefer, which of course is not certified gold. I works for me well. Besides, yarn installing packages each time container comes up does so incrementally (only new packages are fetched and installed). A win-win 🙂