Friday, August 31, 2018

Harp/Jade Debug Snippet

I’m using Harp with Jade recently. At the beginning, it was hard for me to figure out the JSON data structure used by Harp at build time. It was also hard to debug JavaScript function written in Jade and executed at Harp compile time. In the end, I figured out that I could dump that JSON as a string to console.log in browser. Everything is so much easier now.

Now I have that debug.jade file in my project. Whenever I want to examine some JSON data in Harp, I just call != partial('debug', { data: anything }) and pass the right data.

Wednesday, May 16, 2018

Divide Notifications into Interrupt, Reminder and Backlog

iOS push notification

I ran an experimentation for more than a year. I put all of my iOS devices into permanent Do Not Disturb mode. It was a great experience, but I felt I should let very small amount of push notifications through.

My initial intention of setting permanent Do Not Disturb was to avoid distraction in my 1:1s. I could see how other people looked at their phones during 1:1. Sometimes it’s a push notification. Sometimes they were just not paying attention to me. I wanted to avoid doing that, so I put my iPhone into DND mode. My Apple Watch mirrored, so it didn’t make a sound or vibrate either.

My experience with permanent DND was good. I no longer had the unconscious reaction to look at my phone when it vibrated. I didn’t need to put away my phone, because it acted like it never received those push notifications until I proactively look at it. There was one small problem: Somtimes I missed those really important push notifications, and then I missed meeting or a time sensitive message.

Because I was very happy with the silence of almost all notifications, I didn’t want to change that. I only wanted a small patch to let some of them through. That triggered me to think about what my ideal push notification model would be. My conclusion was push notifications should be divided into 3 categories:

  • Interrupt: An interrupt should interrupt whatever transaction I’m in and get me to deal with it immediately. This should be extremely rare, and I shouldn’t miss when it happens.
  • Reminder: An ideal reminder should remind me of things in the perfect context, which usually means right time (in between transactions) and right place.
  • Backlog: This is like an email sitting in my Gmail unopened for years. If I want to take a look, I can. Otherwise, don’t come to me. Backlog should never be pushed. It should be pulled.

These are the ideal categories, but iOS doesn’t allow exact setup like this. I have to tweak a little bit and make it implementable within iOS. There are these constraints:

First of all, iOS push notification settings are on per app basis. Because most apps don’t provide finer granularity on push notification control, that means I have to assign category to apps. If I assign interrupt category to an app, every push notifications from that app is an interrupt.

Second, only built-in apps have complex notification settings for iOS and watchOS. For most apps, there’s only one setting for sound. It’s either on or off. Vibration is associated with sound and they are toggled together. There’s an additional toggle for Apple Watch. It’s all or nothing. That means if sound and vibration is on for iPhone, it’s also on for Apple Watch.

One more thing. Because existing technology can’t remind me in perfect context, I have to allow apps to remind me at its convenience and use snooze to make it wait for the perfect context.

Based on these constraints, here is my setup:

  • Interrupt: Apps in interrupt category can use sound/vibration. They can show notifications on Apple Watch. I have certain messaging apps in this category. They are not my main messaging apps.
  • Reminder: Apps in reminder category can also use sound/vibration. They need to provide a snooze button in notification. They show up on Apple Watch. I have some calendar and todo apps in this category.
  • Backlog: No sound and thus no vibration. Not allowed on Apple Watch. This is exactly the same as permanent DND mode.

Besides, I disabled badge for apps that are abusing it. If an app uses badge to get my attention frequently, I disabled badge for it.

There’s a 4th category that I would call null. Apps in this category use push notification to promote themselves in a way that’s not valuable to me. I disabled push notification for them.

This setup isn’t perfect. Apple doesn’t design iOS to work in this way. Apple gives some level of control to users, but no automation for power users. At the moment, I have to turn off sound for most apps one by one because they are in backlog category. I have to turn them off for Apple Watch, too. If I can make this default settings for all new apps, it could save me time.

This is my new experimentation for 2018. It’s working well so far. Maybe I’ll give another round of update in 2019 and identify new ways to tweak this. I hope iOS and Android have more power ways to manage notifications at that point.

Monday, May 14, 2018

DNS over Phone

This is very impractical, and I did it for fun anyway. If Cloudflare can do DNS over SMS, then somebody is going to build DNS over snail mail some day.

The DNS over Phone setup is very simple. We need a Workflow (iOS) to query DNS over HTTPS. We also need an IFTTT service to call ourselves. That’s it. Below are the Workflow and IFTTT applet you can import immediately:

IFTTT Applet

This applet uses “Workflow” as the trigger and uses “Phone Call (US only)” as the action. It’s built on IFTTT Platform so it’s shareable. It takes one ingredient from the input and announce it in the phone call. That ingredient’s value would be coming from the Workflow.


This Workflow asks for a domain. (If we give it an URL, it extracts the domain from the URL.) Then it sends the domain to Google Public DNS, which provides DNS over HTTPS service. The response from Google Public DNS is in JSON. We want to read json.Answer[json.Answer.length - 1].data from it, because that would be the IP address we are looking for. In the end, we trigger the IFTTT Applet with the IP address as the only ingredient.


Q: Why do we use Google instead of Cloudflare for DNS over HTTPS?
A: They provide JSON response in very similar format. Google’s response has Content-Type: application/x-javascript; charset=UTF-8 header, while Cloudflare’s has Content-Type: application/dns-json. That tiny bit of difference makes Workflow treating Cloudflare’s response as a binary file instead of text. There might be a way to get the text out of a file. When I figure that out I can provide Cloudflare as an option.

Q: Why do we read from the last item of json.Answer array?
A: If the domain uses CNAME record, then json.Answer will contain multiple items. The last item would be the A record pointing to the IP address. Other items would be CNAME records.

Sunday, April 08, 2018

Starting my Patreon experiments

I decided to start two experiments with two Patreon tiers.

The first one would allow patrons to read my blog posts 1 week before they are publicly available on my regular blogs (on I’m not sure how many people would actually buy this, because my blog posts are usually not time sensitive at all. It’s a way to allow patrons to encourage me to write blog posts more often. The 1 week gap is more like a symbolic reward.

The second one is more interesting. I’m opening 1:1 at $100/hr for anybody interested in talking to me. I know some people are willing to pay me to answer questions on Zhihu, but at a much lower price and probably needs much less time. The reason I hesitate to answer those questions is the lack of building trust and relationship. People can get an answer and then go, but that doesn’t creates any long term value. I want to see if I can building long lasting relationships through monthly 1:1 while getting paid at a reasonable rate.

Because monthly 1:1 isn’t really scalable, I limit 5 patrons at this tier. If this becomes popular, I’ll find a solution for the scalability problem. It’s a good-to-have problem, so I don’t think about it for now.

If you are interested in, here’s a link to my Patreon profile. You will be able to find the reward tiers there.