The two-step approach to big code modifications

We all have to make significant code changes from time to time. Most of these code changes are large. Consider the scenario that you merged one such significant change, and then other team members made a few more changes on top. Then a major bug is detected. You desperately make the fix. It makes it in. You declare a victory, and a few hours later, your colleague notices another bug/crash/performance regression. Your commit cannot be reverted. It isn’t just about you. Many others have built on top of the change you made—the code sloths along in this broken state for a few days before you eventually fix it. Everyone has faced this issue at some point or the other.

Read More

Incremental testing: save time and money on CI for monorepo

To use monorepo or not is an eternal debate. Each has its pros and cons. Let’s say you decide to go with monorepo, one major issue you will face over time is slow testing. Imagine a monorepo, consisting of an Android app, an iOS app, some backend code, some web frontend code. On only very few occasions will someone modify more than one of those simultaneously.

Further, most of these projects confined to their directories would be using different build systems as well, for example, gradle for Android, yarn/npm for Javascript, go/rust/java/npm for the backend. The total build time and test time will only grow over time. It annoys developers making small modifications to their part of the codebase. And it slows down the development velocity drastically.

Read More

How to deploy side projects as web services for free

In 2020, the web is still the most accessible permission-less platform. For the past few months, I have been playing and building side projects to simplify my life. I started with a Calendar Bot for scheduling events, DeckSaver for downloading decks from Docsend, AutoSnoozer for email management, and StayInTouch for maintaining follow-ups.

When I started on this journey, I had the following in my mind.

  1. Cost of domain ~ 12$ a year or 1$ a month
  2. Cost of a VM ~ 10$ a month

Read More

Docker 101: A basic web-server displaying hello world

A basic webserver

Docker containers are small OS images in themselves that one can deploy and run without worrying about dependencies or interoperability. All the dependencies are packed in the same container file. And the docker runtime takes care of the interoperability. You are not tied to using a single language or framework. You can write code in Python, Go, Java, Node.js, or any of your favorite languages and pack it in a container.

Consider a simple example of a Go-based webserver

Read More

Troubleshooting Android Emulator: “Emulator: Process finished with exit code 1”

Emulator: Process finished with exit code 1

You opened AVD Manager in Android Studio and tried to start an AVD, you got
“Emulator: Process finished with exit code 1”. Following are the steps to debug
this

  1. Find out the name of the emulator.
    Click the down arrow šŸ”½ which is to the right of play arrow ā–¶ļø, to find out the name of the AVD. Let’s say the name is “Nexus_5X_API_28_x86”.
  2. Try starting the AVD directly from command-line
    It fails with another cryptic error, but that’s at leastĀ more actionable

    $(dirname $(which android))/emulator -avd Nexus_5X_API_28_x86
    ...
    PANIC: Broken AVD system path. Check your ANDROID_SDK_ROOT value [/opt/android_sdk]!

    Let’s retry in the verbose mode to see a detailed error

    $(dirname $(which android))/emulator -avd Nexus_5X_API_28_x86 -verbose
    ...
    Not a directory: /opt/android_sdk/system-images/android-28/google_apis/x86/
    

    So, the system image is missing.

  3. Install system image
    # List all possible system images.
    $(dirname $(which android))/bin/sdkmanager --list
    # List the images we are interested in.
    $(dirname $(which android))/bin/sdkmanager --list | ack google_apis | ack android-28 | ack x86
    # Install the right one using
    $(dirname $(which android))/bin/sdkmanager --install 'system-images;android-28;google_apis;x86'
    
  4. Now try starting the AVD again.
    $(dirname $(which android))/emulator -avd Nexus_5X_API_28_x86 -verbose
    ...
    emulator:Probing program: /opt/android_sdk/tools/emulator64-x86
    emulator:Probing program: /opt/android_sdk/tools/emulator-x86
    PANIC: Missing emulator engine program for 'x86' CPU.
    

    Turns out there is another version of the emulator installed in $(dirname $(dirname $(which android)))/emulator/emulator. And the emulator I was using is a stray one.

    # This works
    $(dirname $(dirname $(which android)))/emulator/emulator -avd Nexus_5X_API_28_x86 -verbose
    

Android: Using “Die with me” app without killing the phone’s battery

Die with meĀ is a chat app which can be used only when the phone’s battery is below 5%.

Here is a fun way to use the app without draining your phone’s battery. Connect the phone via ADB or start Android emulator and fake the battery level to 4%.

sudo pip3 install adb-enhanced
adbe battery level 4 # Set battery level to 4%

And now, you can use the app. After playing with the app, reset the battery level with,

adbe battery reset

 

The first two statements of your BASH script should be…

#!/usr/bin/env bash
set -euo pipefail

The first statement is a Mac, GNU/Linux, and BSD portable way of finding the location of the bash interpreter. The second statement combines

    1. “set -e” which ensures that your script stops on first command failure. By default, when a command fails, BASH executes the next command. Looking at the logs, you might feel that the script executed successfully while some commands might have failed. Caveat: Be careful about applying it to existing scripts.
    2. “set -u” which ensures that your script exits on the first unset variable encountered. Otherwise, bash replaces the unset variables with empty default values.
    3. “set -o pipefail” which ensures that if any command in a set of piped commands failed, the overall exit status is the status of the failed command. Otherwise, the exit status is the status of the last command.

References:

  1. Unofficial Bash strict mode
  2. ExplainShell

Keep your dotfiles bug-free with Continuous Integration

Update: As of April 2020, I have switched over to GitHub Actions. Travis CI has become buggy and flaky over time and I got tired of trying to keep the builds green. My GitHub action scripts can be seen here.

Just like many software engineers, I maintain my config files for GNU/Linux and Mac OS in a git repository. Given that, I wrote a fair bit of them in interpreted code, notably, Bash, it is a bit hard to ensure that it is bug-free. The other problem I face is that packages on homebrew, the Mac OS package manager becomes obsolete and gets deleted from time to time.

I added CI testing on Travis CI to prevent these breakages and to ensure that my dotfiles are always in good shape for installation. The great thing about Travis CI is that it is entirely free for open-source repositories even for testing on Mac OS containers.

Read More

Circle CI vs Travis CI

Update: As of Mar 2022, I recommend everyone to use GitHub Actions

I maintain a somewhat popular Android developer tool (adb-enhanced). The tool is written in Python, supporting both Python 2 and 3. Testing the tool requires both Python runtime as well a running Android emulator. I, initially, used Travis CI for setting up continuous testing of this tool. Later, I felt that Travis CI was too slow and when I came across Circle CI, I decided to give it a try. As of now, both Travis and Circle CI are used for testing. Here is what I learnedĀ from my experience.

Read More