Justin Duke

Creating a password validator component in Vue

Sometimes you procrastinate one feature by writing another. Even if that other feature is pretty small and inconsequential. 1

For me, that other feature — if you can even call it a feature — was password strength testing for Buttondown.

Read more →

Side Projects: July 2017


  1. Buttondown continues to grow, as does Spoonbill.
  2. Consolidated my web stuff. This site is better than ever, Village Blacksmith is nice and clean.
  3. Still launching dope stuff on Buttondown: I’m particularly proud of its new embed support, which will hopefully integrate with Medium somewhat soon.


  1. Buttondown continues to be unprofitable, as does Spoonbill.
  2. I didn’t launch Very Cute, though I got a bit more progress on it. (I don’t feel that bad about this, because it was a productive month — it’s just that most of my cycles were poured into Buttondown.)

What’s Next

  1. Going to retool Buttondown’s pricing to try and monetize the majority of the user segments who don’t qualify for the current pricing schema. For $19/mo, you can set a custom redirect URL, remove branding, and probably a couple other things.
  2. Sticking to that. I think with the current amount of energy I’m spending on Buttondown it’s prohibitively difficult to work on something else. (More about this later.). There are a couple projects I want to do (React Native version of https://coinmarketcap.com/, public Airtable tracking SaaS prices, simple AWS infoproduct…) but I don’t think I can commit to any of them right now.

Other Thoughts

  • The past couple weeks have been exhausting! My schedule has felt packed to the gills: wake up early, do chores and administrative stuff til 9, then work til 1, then work out, then work until 5 or 6. At that point I’m exhausted and have to choose between working on side projects or doing something fun and active. I’m been choosing the latter as much as possible — which is the right choice — but it means I don’t move the needle as much as I’d like, which means I feel guilty.
  • I’m really tempted to start tracking vanity metrics more, because they’re doing really well. But vanity metrics won’t buy me scotch.

Read more →

Creating a reusable tabbed card component in Vue

I built a thing for Buttondown that lets you embed an iFrame to handle subscriptions really nicely:

Read more →

There are still dragons everywhere

I’ve been programming for the better part of a decade, and I’ve been programming in Python for the better part of that better part.

As loathe as I am to assume the mantel of “X Engineer”, if I were to describe my career in relation to any technology it would be “Python Engineer”.

It is the language I feel most comfortable with; it is the language I reach to first when starting a new project; it is the language I write daily. It is as frictionless as English.

And yet I still find myself losing hours to things I thought I had mastered.

The other night, I played with the idea of bumping Buttondown to Python 3.6 from 3.5 — I wanted cooler types and faster dictionaries, and it seemed like a painless process, so I ran brew upgrade python and was off to the races.

Except something broke: there was an issue with how I was using grequests that was fine in 3.5 but not 3.6, and I decided to revert.

So I hit up pyenv to try and grab 3.5 (since it was no longer the latest thing installed on my machine) and use that to recreate my virtualenv.

Except now psycopg2 was broken for some reason?

It kept on throwing a bizarre error that had like, three matches on Google total:

ImportError: datetime initialization failed

So, I do what I’ve learned to do: nuke everything and start over.

Except now virtualenv is broken, looking for a global python installation that doesn’t exist: no such file or directory, it snaps back at me.

After a few hours of fruitless Googling and tinkering, I do the thing I’m not supposed to do: I just re-install the old version of Python with brew:

brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/ec545d45d4512ace3570782283df4ecda6bb0044/Formula/python3.rb

And suddenly everything works. I recreate the virtualenv; I reinstall the dependencies; I get back to work.

There’s no big lesson here. I probably should have stuck with 3.6 and figured out the grequests issue; I probably should have been more disciplined with installing 3.6 through pyenv and managing my environments.

But I think it feels good to get lost in a sea of arcana and development hell for an afternoon. I mean, it sucks in the moment — there’s no denying that. But once the moment passes, you’re reminded about how much left there is to learn and to master.


App sizes are under control, they just aren't under scrutiny

I wrote at the first of last month about bloat in apps, in response to a thoughtful piece by Ben Sandofsky about how he minimized bloat in Halide.

I had some misgivings with the post and the ideas it espoused — namely, that “bloat is bad” is a reductionist truism — but still admired the approach Sandofsky took and the clear tradeoffs and sacrifices he discussed.

This post by Trevor Elkins making the rounds today has the same message, though it lacks the nuance and pragmatism:

As app developers, we should be more conscious of the space we use. Take some time to remove the cruft that builds up and push back against needless waste.

So really, how does an app that occasionally sends me a connection request and recruiter spam take up 275MB? Maybe I’ll do an analysis at some point, but for now it’s deleted.

Trevor works for Kayak, whose iOS app weighs in at 176 MB.

Perhaps Trevor is actively working on reducing the size of the app bundle, in which case he knows how difficult it is to make decisions like do we spend two weeks reimplementing this framework or just eat the 5MB it adds to our final app size?

Or perhaps he isn’t, but he’s talked with fellow Kayak engineers who have determined that the bloat is worth the cost — maybe they’ve got a lot of high-resolution assets, or have decided that the development velocity a framework-heavy approach enables is worth the drop-off in installs.

Or perhaps he didn’t even know Kayak’s app size is so large in the first place, because it’s a factor that doesn’t impact the majority of users after the initial download. 1

Either way, it’s kinda shitty to insiniuate that an app is bloated due to incompetence; it’s kinda shitty to insiniuate that an app is bloated due to indifference.

Everything is a trade-off. Some companies don’t think it’s worth it to slim down their bundle sizes; some companies do. It’s disingenuous to pretend otherwise.

(For what it’s worth, I think the Kayak iOS app is great.)

  1. Which isn’t to say that it doesn’t impact users — it’s to say that the surface area of the impact is pretty small. [return]

The best podcasts

First, some background and context, because I’m a super persnickety podcast listener:

  1. I think podcasts, like anything else, need to be interesting and timeless. If an episode is useless or pointless to listen to three months after its publication, then its a bad episode; if that describes most episodes, then it’s a bad podcast for me. 1
  2. I don’t like most politics or tech news podcasts, mostly because they end up guilty of the above.
  3. I don’t like comedy podcasts, much to my chagrin. 2
  4. In my experience, most interview-style podcasts are more marketing/PR than actual information. Obviously that’s a broad brush (and a couple of the podcasts on this list are interview podcasts!), but in general I feel like the ROI on interviews are low.
  5. This list is constantly evolving. It was last edited in August of 2017. (And I’m always looking for new things to listen to!)

Read more →

A nice little list of valid and invalid email addresses

Note This is a growing and evolving list! If you see anything that I'm missing, email me and I'll add it.

I’ve been spending a lot of time working on Buttondown lately and, as you might expect for a newsletter app, lots of this work revolves around dealing with email addresses.

In an effort to save myself and others some time, I’m trying to collate a list of usefully distinct and weird invalid and valid addresses for test harnesses and such. Below is what I have so far: it’s not a complete set, but it’s comprehensive enough to handle most of the weirdnesses out there.

Read more →

Everything I got wrong with Buttondown's launch

I want to do an earnest, thorough analysis of Buttondown’s launch at some point in the future: what the traffic was like, conversion rates, et cetera. (Truth be told, I haven’t even looked at those numbers yet — there were a bunch of visitors, there were a bunch of signups, and that’s pretty much all I know because I’ve been so in the weeds with work the past week and a half.)

That’s not what this post is, though that post is coming! Instead, I wanted to talk about the question that I always wished more people talked about during launch postmortems:

Where did I mess up?

Read more →

So good it makes you angry

Sometimes there are things that are so good it actively makes me angry, because it presents a version of the form that seems unimpeachable.

Money Things is like that — it is such a good newsletter, so dense and interesting and funny that it makes me mad, because having to read, like, Hot Pod or Pro Rata or other industry newsletters after it is just an exercise in dwindling returns.

Lincoln in the Bardo was like that — it was such an interesting and beautiful and novel piece of work that it retroactively invalidated so much of what I had consumed beforehand. It was my first audiobook, too, and it basically ruined audiobooks for me, because how do you top that?

And now, this. This product page for Transmit 5.

Let’s run through all the things it does well:

  • There’s a really dope spinning truck on the top. You can move it with your cursor. It captures the eyes, it is pretty, it is neat. It is a great truck.
  • The copy is clear, concise, and large.
  • There are screenshots of the product front and center, because obviously there should be.
  • It’s got those nice little infographics about why you should upgrade (answering probably the third most important question this page has to answer, which is Why should I upgrade?)
  • The page flows nicely, with the little arrows between each content block that lends a little flow.
  • All of those features! And the nice snippet screenshots for the keynote features (but the nice little icons for all the features.)

And the subtler things, too:

  • It is dynamic, but not busy. There is only ever one thing to look at at once.
  • Super responsive, and not in a “uh, I guess everything is gonna be in a smaller font now” kind of responsive way.
  • The way it repositions the entire product from most folks’ perception of it:

Now, long ago we’d call Transmit an “FTP client”, but today, with Transmit 5, we connect to lots of different server types and cloud services. For example, Amazon S3 dramatically changed the way files are served on the internet, and Transmit is a great way to manage your Amazon S3 buckets.

  • Seriously, that truck is so cool!

Like, ugh.

This is like a heat check of a product page. I have bookmarked it and will revisit it every week or so when I need reminding that these types of things can be so much better than they usually are.


Response Times

Jakob Nielsen wrote in 1993 about the three important demarcations in response time. This is a terrific article that I come back to every year or so and see how my interpretation of it has evolved, like reading an old book to see how your perspective of the narrative changes over time.

0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.

1.0 second is about the limit for the user’s flow of thought to stay uninterrupted, even though the user will notice the delay. Normally, no special feedback is necessary during delays of more than 0.1 but less than 1.0 second, but the user does lose the feeling of operating directly on the data.

10 seconds is about the limit for keeping the user’s attention focused on the dialogue. For longer delays, users will want to perform other tasks while waiting for the computer to finish, so they should be given feedback indicating when the computer expects to be done. Feedback during the delay is especially important if the response time is likely to be highly variable, since users will then not know what to expect.

Those three benchmarks were set in 1968; Nielsen was commenting on how remarkable it was that 25 years later they still held mostly true.

Now it’s a bit more than 25 years after 1993, and even now these seem pretty accurate, right?

You can argue that web applications (and, even more so, native apps) demand a certain level of vivacity that you can’t achieve with a full second or even a tenth of a second, but I think Jakob precisely nails the important way to look at “sluggishness” in his 2014 update to the piece:

0.1 second: Limit for users feeling that they are directly manipulating objects in the UI. For example, this is the limit from the time the user selects a column in a table until that column should highlight or otherwise give feedback that it’s selected. Ideally, this would also be the response time for sorting the column — if so, users would feel that they are sorting the table. (As opposed to feeling that they are ordering the computer to do the sorting for them.)

Sorting the table, as opposed to ordering the computer to sort the table.

It’s not about the speed: it’s about the directness — about the frictionlessness — about the sensation that there are no layers or conduits between you and your task.

© 2017 Justin Duke • All rights reserved • I hope you have a nice day.