Technology is awesome. Sometimes we just design things in a stupid way that unnecessarily limits the use of an otherwise awesome product. Apple does that a lot lately.

iOS is amazing. It’s fast, it’s efficient, it has incredible user retention, it’s reasonably visually appealing (minus some obvious contrast issues), it’s secure… all good.

But what if you don’t want to use Safari? You can install Chrome, but it’s just a Safari WebView with Chrome’s sync. Same with Firefox. Up until recently, third-party apps couldn’t even use the full Safari engine, severely limiting JavaScript performance.

Maybe you want to download a file? That seems simple enough. Except you can’t. iOS doesn’t allow you to save anything other than images from websites without third-party applications that are limited in what files they support as well. Even if you do manage to download a file, good luck opening it.

My biggest issue with iOS is one most people will probably not have even noticed. Try unlocking your screen and seeing how long it takes before the device responds to any other user input. It’s about 2 seconds. You’re waiting for an animation that does nothing. Try pressing the Home button from an app. Notice that delay? That one’s more practical - it waits to see if you’re going to press it twice, to enter the multi-tasking view. Still, after using Android for a while, that second delay is annoyingly long. Android doesn’t need to wait when pressing the home button because button clicks are unambiguous. Rather than use one button for several actions, each does one specific thing.

Customizing iOS is basically impossible. Jailbreaking supposedly makes this easy, but that’s mostly a luck game. Apple patches jailbreak exploits like they’re the worst thing that could possibly happen to them, instead of just letting users choose to unlock their devices with a big warning like Android does. If you do get lucky enough to be running the right version of iOS when a jailbreak is released, you better not be interested in updating for the next year or so.

The new MacBook Pro is nice. It’s incredibly thin and lightweight. I’m mostly okay with removing USB type-A ports too. Type-C is the future and we may as well embrace it.

That Touch Bar though. It’s so pointless. 95% of it’s functionality is just a shortcut to functionality already available via a hotkey, only you can’t feel for it and it’s not in a consistent place between applications.

Weirdly, the Touch Bar feels cheaply implemented in person. On Apple’s renders, it looks beautifully designed and seems to sit flush with the keyboard. The actual LCD is visibly lower than the keys, and seems to be much lower quality than the main display, which makes it seem like an extra add-on rather than a well-integrated feature.

I also miss my SD card slot, and MagSafe, while proprietary, was really nice.

MySQL Backups

MySQL is pretty nice for a free, Open Source RDBMS. Before trying any kind of management, you should totally have a .my.cnf file in your ~. Put your username and password for localhost there, and then remove read permissions from everyone but yourself. This file specifies the default options to use with MySQL command line tools, making them much easier to work with, and avoiding having to repeatedly type in your password or accidentally letting it end up in a history file somewhere.


Flexible backups with mysqldump

The mysqldump tool is fairly straightforward, but you’re likely not using it to it’s full potential.

To make working with your export easier (making structure adjustments and such), it’s often useful to export the schema and data separately. A --no-data dump will include table schemas and view declarations, while a --no-create-info backup will only include table data.

mysqldump --no-data dbname | gzip > dbname-schema.sql.gz
mysqldump --no-create-info dbname | gzip > dbname.sql.gz

If you plan on changing your column ordering or adding columns from your schema file, your inserts in the data backup will no longer import correctly. To work around this, you can use the --complete-insert flag, which includes column names in the inserts, ensuring they restore properly as long as all the columns backed up are still present in the new table.

mysqldump --complete-insert --no-create-info dbname | gzip > dbname.sql.gz

Restoring views to a new server can fail if the view’s DEFINER is not a user on the new system. If you’re going to be importing the database on a different system with potentially different users, you can use grep to filter out the DEFINER rows, ensuring views import without errors.

mysqldump --no-data dbname | grep -v "50013 DEFINER" | gzip > dbname-schema.sql.gz

I pretty much always pipe mysqldump through gzip since there’s no good reason to keep an uncompressed export sitting around. Restoring gzipped backups is very simple to do in-place with the help of zcat.

zcat dbname-schema.sql.gz | mysql dbname
zcat dbname.sql.gz | mysql dbname

If you’ve got any stored procedures, triggers, or functions in your database, these will only be backed up if you use the --routines flag. I prefer to keep these in another file.

mysqldump --routines --no-data --no-create-info dbname | gzip > dbname-routines.sql.gz

If you’re using BLOB columns with unfiltered data, you may run into issues restoring backups of that data. To work around this, you can store those in hexadecimal which, while larger by default, shouldn’t take up too much more space once gzipped. Enable hex encoding with the --hex-blob flag.

mysqldump --hex-blob --no-create-info dbname | gzip > dbname.sql.gz

If your database consists entirely of InnoDB tables, you have the option of using a transactional backup, which ensures data integrity without requiring table-level locks! This can be enabled with the --single-transaction flag, but keep in mind any non-InnoDB tables will not be locked, and may be backed up inconsistently.

mysqldump --single-transaction --no-create-info dbname | gzip > dbname.sql.gz

Multi-threaded backups with mydumper

If all you’re looking for is a crazy-fast backup of a large database’s structure and data, you should be using the amazing mydumper tool, which uses a multi-threaded approach, backing up each table in a separate thread.

It’s fairly straightforward to use. Let’s do a gzip-compressed single-database backup.

mydumper -c -B dbname -d export_dir

Restoring a mydumper backup is quite simple

myloader -d export_dir

Mydumper also has powerful options like regex table name matching and row-count file splitting. Read the man page for details on everything mydumper and mysqldump can do.

Expand a live ext4 filesystem

So maybe you let your OS installer configure your partitions for you because you’re lazy like me. And maybe you realized it created a 16 GB swap partition on your tiny SSD. And maybe you wanted / to use that 16 GB.

Luckily, Linux is awesome.

root@box:~# lsblk
sda      8:0    0 111.8G  0 disk
├─sda1   8:1    0   512M  0 part /boot/efi
├─sda2   8:2    0  97.3G  0 part /
└─sda3   8:3    0  15.9G  0 part [SWAP]

/dev/sda2 is the partition we want to extend. We’ll use gdisk.

First, list the partitions to be sure we’re starting in the right place. Make a note of the start sector for the partition you want to extend, as we’ll be deleting it and creating a new one in it’s place.

root@box:~# gdisk /dev/sda
Command (? for help): p

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624       190093083    97.3 GiB   8300  Linux filesystem
   3       190093084       234441614    15.9 GiB   8200  Linux swap

Next we’ll delete the swap and root partitions.

Command (? for help): d
Partition number (1-3): 3
Command (? for help): d
Partition number (1-2): 2

Create a new partition. Use the start sector from old partition as the first sector on the new one, leaving the default values for the last sector and partition type.

Command (? for help): n
Partition number (2-128, default 2): 2
First sector (34-234441614, default = 1050624) or {+-}size{KMGTP}: 1050624
Last sector (1050624-234441614, default = 234441614) or {+-}size{KMGTP}:
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300):

Print the new partition table to make sure everything looks right.

Command (? for help): p

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624       234441614   111.3 GiB   8300  Linux filesystem

If it’s all good, write the new partition table to disk!

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING

Do you want to proceed? (Y/N): y

Your new partition table is written, but the system won’t recognize it everywhere yet. We’ll partprobe to fix this.


Now your new partition table should be live.

root@box:~# lsblk
sda      8:0    0 111.8G  0 disk
├─sda1   8:1    0   512M  0 part /boot/efi
└─sda2   8:2    0 111.3G  0 part /

Finally, we’ll resize the filesystem to fill the new partition.

resize2fs /dev/sda2

Arch on UEFI

UEFI boot is weird, mostly because of backwards compatibility. Here’s a simple guide to setting up UEFI on GPT, assuming you already know how to do a typical Arch install.


Gdisk supports GPT, so we’ll use that to partition our system disk.

gdisk /dev/sda

Press o to create a new GPT partition table, overwriting any existing contents.

Then, use n to create new partitions:

  • Root partition (8300), full disk size - 1.5 GB.
  • EFI partition (EF00), 1024 MB

Type p to output your new partition layout, and w to write the changes.

Finally, refresh the disks.



Create filesystems for the new partitions:

mkfs.ext4 /dev/sda1
mkfs.fat -F32 /dev/sda2

Mount the new filesystems

mount /dev/sda1 /mnt
mkdir /mnt/boot
mount /dev/sda2 /mnt/boot


First, mount efivarfs if not already mounted:

mount -t efivarfs efivarfs /sys/firmware/efi/efivars

We’ll use systemd-boot, as it’s included within systemd.

bootctl install

We’ll need to write a few new config files in /boot/loader:

vim /boot/loader/loader.conf
default arch
timeout 3
editor  0
vim /boot/loader/entries/arch.conf
title   Arch Linux
linux   /vmlinuz-linux
initrd  /initramfs-linux.img
options root=/dev/sda1 rw

Finishing Up

exit # ^D is always superior :P
umount -R /mnt


Over the last few years, I’ve worked on an open source project management system called Phproject. It’s on GitHub under a GPL license, and you should use it.

Apparently I did a good enough job of it that someone tried to sell it. A Slovakian team going by the name KreaHive rebranded it as “Workflow”, and posted it to CodeCanyon. Apparently it was removed from the site quite quickly as I never saw it active, but I found it quite funny. After a bit more research, they have a public revision history that perfectly matches my latest updates on RevCTRL, and even made it to a business software review site, Capterra.

Workflow on Code Canyon

Then it got even better. I found a supposedly “nulled” version of Workflow on a site called Guest Post. Sadly the download links just redirect you back to the Code Canyon site, I would’ve loved to see what code changes were made in a nulled version of a stolen version of my open source project. At least they linked to a neat marketing graphic for it.