Cappuccino App  

This new reading app from the developers of Airmail looks promising. I’d love for it to replace Unread, which is an elegant app but has no character.1 The biggest thing I’d love to see changed about Cappuccino is the ability to read an entire blog post inside the app. Right now it only shows an above-the-fold excerpt. When viewing an article that is itself a link elsewhere, tapping the headline in Cappuccino takes you to the linked article, not the article that’s reposting it, which means there’s no way to view the full contents of the latter. Still though, it’s a great 1.0 MVP and I’m looking forward to the future polish.

The website cracks me up. Here’s the CSS font declaration:

* {
    font-family: avenir !important;

Since I use a Windows for work, this means the site renders as serif. I adore the fact that this team felt no need to try to make the website look decent on Windows. Their customer is someone who uses Apple products, so why should they care how it appears on Windows?

  1. Well, that and it’s used by a crowd that I want to distance myself from because of how laughably ridiculous they’ve gotten in their brazen, nonsensical SJW politics. That’s being reactionary, but whatever. It’s for this same reason that I despise Tweetbot (or I would if I still used or cared one ounce about Twitter, which I do not). ↩︎

Thoughts on GDPR Compliance  

For a while I’ve been wanting to build out a little web page that “types” out text to the screen and decided to do this in tandem with some thoughts on the General Data Protection Regulation (GDPR) that the EU is enforcing later this month.

One fun part of this was to make sure that the text doesn’t print out in a perfectly linear manner, since humans don’t type that way. Think of this as the moral equivalent of the hems and haws in the Google Duplex assistant.

The whole page resides in a single index.html file so feel free to poke around and look at how I built it. The callbacks in the JavaScript got pretty nested but I wanted to keep this as simple as possible and make it compatible in as many browsers as possible without having to resort to a polyfill for async/await.1

  1. Internet Explorer, I’m looking at you. ↩︎

Activating Buttons with the Keyboard in macOS  

Conorgriffin, writing at Stack Exchange:

For many dialog boxes you can select the option you want by pressing ⌘+firstletter where firstletter is the first letter of the option you want to select

I wonder how many macOS users know this combo, as well as the ⌥+⌘+Delete combo for rapidly deleting multiple threads in Messages.

What’s New In Chrome 66 DevTools  

If you make a living writing software that runs in a browser, this is mandatory viewing. The Chrome team is making the world’s best browser more and more great, again and again.

As an aside, it’s intriguing to me that Kayce’s sample at the end is using VueJS, not Angular. Just shows you just how influential Vue is becoming in the developer community.

Concerns Over the UI and UX of Windows 10 Compared to macOS

  • First there is the plague of rich text formatting when pasting from the clipboard. It persists by default across all the major applications, which is wrong; the clipboard should copy text as plain text, not rich text. There are many, many times in chat messaging and email where it is obvious that data was copied and pasted and took on an unwanted form. There are very few instances in which this is actually desired. This problem afflicts Skype for Business, Outlook, and OneNote, among others.1 2
  • Related, selecting text for the clipboard is a hassle. You know how it’s hard to select text in iTerm in macOS? Imagine if every major app was that hard to select, and that gives you an idea of how hard it is in Windows 10. By the time you’ve managed to actually select and copy some text, you don’t even care that it’s going to come out as rich text formatting. You’re just thankful you didn’t have to type it out all by hand like a slave. The software makes you work for it.
  • The operating system contains zero rounded corners. This is wrong. It creates mental fatigue. macOS correctly understood early on that rounded corners are important. Andy Hertzfeld:

    Steve [Jobs] suddenly got more intense. “Rectangles with rounded corners are everywhere! Just look around this room!”. And sure enough, there were lots of them, like the whiteboard and some of the desks and tables. Then he pointed out the window. “And look outside, there’s even more, practically everywhere you look!”. He even persuaded Bill to take a quick walk around the block with him, pointing out every rectangle with rounded corners that he could find.

  • Split screens are a nightmare to work with. On macOS, when you have two windows that share a screen, and you go to resize them, they always stay connected, and the blur that occurs on one of the windows is beautiful. On Windows, when you go to resize, an ugly vertical bar is the only thing that appears whilst you move your cursor, and then the windows redraw only when you release your cursor. They divorce very easily too, and while getting them glued back together is achievable, it’s a feat of patience. The software makes you work for it.

  • The methodology for getting to the unlock screen is archaic and idiotic. On macOS you merely tap any key on the keyboard, or move the mouse or trackpad, and you’re presented with the login screen. On Windows, you have to hit a key combination, CTRL + ALt + DELETE, before you can get to the login screen. This key combination is difficult to achieve with a single hand, and there is no need for it. You save no battery by making it harder to get to the login screen - tapping a key wakes up the computer just as much as it does on macOS. Rather, it merely creates an unnecessary step. It’s wasteful. It’s inefficient. Much of the OS is like this. I’m sure there’s a lengthy business explanation about why it’s needed, about how it came into being, about why it makes sense, about why Microsoft can’t get rid of it for reasons X and Y. But you know what? macOS figured out how to not need it, and macOS is used in business a lot, so it’s a solvable problem.

  • There is lunacy in having to open a second window by re-opening the same application. If I’m in Terminal on macOS and I want a new window, I just hit CMD + N. But in Windows, if I’m in Command Line and I want a new window, I have to go to the Start menu, search for it in the cluttered menu system, and reopen the application as though I have zero windows open. This is wasteful and counterintuitive.

  • The OS gets easily confused by having a non-high-density external monitor connected to a high-density laptop display. For example, let’s say you have a Chrome window open on the external monitor, and you accidentally hit CTRL + S, since the keys are mapped incorrectly on Windows. The dialog for saving the window appears at twice the size (i.e. it displays at @2x resolution of what it should be, thereby making it twice as big) because the OS thinks you’re handling this on the laptop high-density screen. Maybe this is just a Chrome bug, I don’t know. All I know is that this sort of thing happens a lot on Windows and it never happens on macOS on an identical setup.

  • And then there is the inferiority of the scrollbars that plague every application in Windows. On macOS and iOS, the presence or absence of a scrollbar within a UI does not affect the overall available width of that UI’s inner content, but it does in Windows 10. This means that text on a web page jumps around when the width changes. This occurs quite often. If you toggle something that adjusts the height of the page content, such that it goes from all fitting above the fold to not all fitting above the fold, you’ll introduce a scrollbar, and thereby inadvertently change the page width, which shifts the entire page content to the left. This also occurs when you open a hamburger menu or modal that overlays the screen and locks it with body: { overflow: hidden }.

There are other problems of course, but these are the ones that I’m reminded of every time I use Windows.

My experience is that most Windows aficionados either have never realized these innate problems with the operating system, or else they’re aware of them but simply don’t think they’re that big of a deal. But if you’ve used macOS for thousands of hours and can appreciate the difference between good software that works for you versus poor software that you must work for, you’ll see a night and day difference between the two operating systems. Windows has had many years to catch up with macOS and it hasn’t. At this point I’m not sure it ever will.

Steve Jobs in 1995:

The problem with Microsoft is they just have no taste. They have absolutely no taste, and what that means is, and I don’t mean that in a small way, I mean that in a big way.

23 years later, this still holds true.

  1. OneNote is truly a train wreck of an app, as is Skype for Business, but I won’t go into app-specific problems in this piece. ↩︎
  2. In fairness, this problem is a scourge for Notes in macOS. It’s largely for this reason I find Notes to be unusable (well, that and its unbearable color scheme and use of text shadow). Notes is one of the weakest applications in macOS. Thankfully it’s the of the very few default apps in macOS that doesn’t adhere to plain text formatting when pasting from the clipboard. ↩︎

It’s Not About the Code

The writing of software is only a small part of being a software developer. The other things that surround this — the human factors, the acceptance of business leadership and decision making that are outside the developer’s control, the grasping of the backstory of how a code base came into being in the precise way that it did, and the comprehension of why a change needs to be made and how and where to make it — all of these things combine to describe what it means to be a software developer. The actual writing and shipping of code is the very last step in an important process that involves many persons within a company, all of whom possess varying degrees of leadership and influence. This upstream process, though sometimes deemed by the developer to be tedious and plagued with inefficiencies, is crucial; it’s what separates startups who one day hope to be relevant from established companies who pay their way, make profit, and drive industry.

Any intelligent punk can write new code from scratch. That is what is taught and learned in university. That is the fun part, the easy part. Knowing how to implement software change in a preexisting, and therefore antiquated,1 project that’s already being used in a production context is the part that brings home the bacon. Knowing how to do this within the confines, constraints, and challenges of a technical team whilst striving to maintain integrity in the code is the job of the software developer. It’s a high calling, and the biggest mistake a developer can make is to presume that it’s all about the code. The code is a means, not an end. The code needs to be the best is can possibly be, of course; but the code is not the purpose for which the company exists. It’s not about the code.

  1. Any software that has managed to survive long enough to see the light of production is by definition antiquated. ↩︎

How to Setup CSS Modules in Aurelia and Webpack 4

As of this writing, if you google “css modules in aurelia” the top two results are:

  • An NPM package that is deprecated.
  • A Medium article where the guy says “I haven’t beaten this one yet, but I’m getting close.” Then later on, “I’ll have to follow up with another post if I am ultimately able to get it all playing together nicely.” Which he never did.

If you dig into the comments of that Medium article though, you’ll find the solution by Sayan Pal, which I’ve confirmed in a working project. Essentially the solution lies with something like this:

// webpack.config.js
const cssRules = [
    loader: 'css-loader',
    options: {
      modules: true,
      localIdentName: '[name]__[local]',
  // You can omit this loader if you're not wanting to support SASS.
  // But you should be wanting to support SASS. :)
    loader: 'sass-loader',

module.exports = {
  // Obviously your actual config file will have more than this in it.
  // This just demonstrates the modularized piece of it.
  module: {
    rules: [

In Sayan’s example, his localIdentName also includes a ___[hash:base64:5] piece which I personally don’t think you should ever need. You shouldn’t ever have two CSS files that are named the same thing, just like you shouldn’t ever have two view-models or views that are named the same thing, and CSS files have a one-to-one correlation to those if you’re going the modularized route. Hence, the double guarantee of uniqueness by appending a hash at the end of the classname merely serves to bloat the DOM and the resulting stylesheet, and it’s wasteful. But I digress.

Let’s say you create a hello-world.scss file that looks like so:

/* hello-world.scss */
.component {
  padding: 25px;

Next, per Sayan’s example, you’ll have a view-model that looks something like this:

// hello-world.js
const styles = require('./hello-world.scss');

export class HelloWorld {
  styles = styles;

Then in your view you’ll have something that looks like this:

<!-- hello-world.html -->
  <div"styles.component">Hello world</div>

On Aurelia’s roadmap for 2018 is to get single-file component support. From the blog:

While Aurelia has focused primarily on optimizing UI workflows for teams working on the same components in parallel, we realize that smaller teams and individual developers have different workflows and needs. To address this, we’re hoping to enable a single-file component development story in 2018.

When that day comes, this solution should continue to work. Until then, you might wish you could import the CSS directly into the HTML files via a require tag, instead of importing it into in the JS files. After all, when the views can play musical chairs with the view-models, you’re allowed a level of dynamism that you can’t have with single-file components; you can mix and match different views with other view-models. Instead of a one-to-one relationship you can have a many-to-many relationship.

There are a couple of problems with importing modularized CSS into HTML files in Aurelia, however. First, you can’t extract CSS via the mini-css-extract-plugin when you’re using this sort of syntax in your HTML files:

<!-- hello-world.html -->
  <require from="./hello-world.scss" />

At least, if it’s possible, I haven’t seen how.

Moreover, even if you could do that, I’m don’t know how you would associate that CSS to its view-model instance. Clearly, modularized CSS in Aurelia can only work in the context a specific (i.e. non-dynamic) view-model. Unless of course you manually hand-write the compiled class names, like so:

<!-- hello-world.html -->
  <div class"hello-world__component">Hello world</div>

But nobody wants to do that.

The good news is that this will be a moot point once single-file components come around. We won’t be thinking in those categories at that point; we’ll have a one-to-one understanding of views and their view-models, which is (respectfully) how it should have been all along.1

  1. When it comes to Aurelia’s MVVM pattern, every single nifty, elegant thing you can do with a many-to-many relationship of views and view-models you can achieve with a one-to-one relationship. That’s a separate topic for another time. For now, suffice it to say that the secret is in having smart components and dumb components, i.e. HOC. ↩︎  

For years, whenever I’ve needed to create a CSS gradient I’ve this tool, which has served well. But of late, a new tool has come to the forefront. When you google “css gradient” the first result is now Today I checked it out and I was floored. Every now and then you enter a place that you immediately can tell has been carefully assembled by someone who really knows their craft. This is one of those places. The site can sing and dance. It’s built by a designer named Moe Amaya. He’s in a whole other league and I recommend checking out his other stuff too. Meanwhile, this gradient site is the site for all things gradients. It’s such a delightful app. Fantastic work.

The Dots Do Matter  

Fascinating story. You can argue that this is primarily Google’s fault, but if I were Netflix, I would change the product so you’re required to confirm your email address. If Netflix enforced that then this entire story could never have occurred. Confirming your email address is standard app behavior. I’m surprised Netflix doesn’t do this.

GitHub Requests a Username Change  

If I had to guess, the engineers were working through a weird bug or security patch issue and had a facepalm moment when they realized that the solution was to remove the malware username. It’s an embarrassing enough situation that I get why the company wouldn’t feel comfortable divulging to Joël the nitty gritty reasons of why the change was necessary.

Or maybe the change was just a stuffy corporate change dictated by the cyber security team. But I like to think it was the former.

Fibonacci in JavaScript

Apple’s event in Chicago last month involved some fibonacci code. This intrigued me to refresh my memory at Wikipedia as to how the algorithm works, and then I played around with some JavaScript implementations for generating a user-specified sequence of fibonacci numbers. The terse two-line version looks like this:

const fib = (amount = 15, nums = [0, 1]) => {
  nums.length >= amount ? nums.length = amount : nums.push(nums[nums.length - 1] + nums[nums.length - 2]);
  return nums.length < amount ? fib(amount, nums) : nums;

In real life, that’s too terse; I’d have a hard time approving a PR for a real app that had that sort of code in it. Here is a more reasonable implementation:

const fibonacci = (amount = 15, numbers = [0, 1]) => {
  if (numbers.length >= amount) {
    numbers.length = amount;
    return numbers;
  numbers.push(numbers[numbers.length - 1] + numbers[numbers.length - 2]);
  return fibonacci(amount, numbers);

Moreover, if we’re not worried about the edge case scenario in which the user enters fibonacci(n) for all n < 2, then we can simplify the function to this:

const fibonacci = (amount = 15, numbers = [0, 1]) => {
  if (numbers.length >= amount) {
    return numbers;
  numbers.push(numbers[numbers.length - 1] + numbers[numbers.length - 2]);
  return fibonacci(amount, numbers);

If we want to find the fibonacci at a specific number, we can simply reference the above function in a convenient wrapping function like so:

const fibonacciAt = (nth) => {
  const numbers = fibonacci(nth);
  return numbers[numbers.lenth - 1];

Or we can modify the core function to achieve the same thing, like this:

const fibonacciAt = (nth = 15, numbers = [0, 1]) => {
  if (numbers.length >= nth) {
    numbers.length = nth;
    return numbers[numbers.length - 1];
  numbers.push(numbers[numbers.length - 1] + numbers[numbers.length - 2]);
  return fibonacciAt(nth, numbers);

What’s interesting is that this Medium article tries to demonstrate a performant recursive implementation to arrive at an nth fibonacci number, but its most performant implementation still requires n * 2 function calls to arrive at the answer; whereas mine only requires n function calls.

Windows Is Alive and Well  

If anyone thinks that Windows is on its way out, they’re not paying attention to the numbers. Windows has 81.9% market share in desktop operating systems. macOS has 12.55% market share. It’s downright amusing how much insular thinking there is on this subject.

Like Facebook and the President, Windows isn’t going anywhere.

Facebook’s Real Mistake  

Facebook wanted desperately to be a platform company. Thus years ago, it wrote an API that was very generous in how much data it gave out, in order to attract developers. Eventually it became clear to the company that it should instead be an advertising company, so it updated its API to be more protective of its data instead. A platform company wants to be generous with its data; an advertising company wants to be stingy with its data. During those intermediary years, companies like Cambridge Analytica took full advantage of the richness of Facebook’s platform-centric APIs.

When foreshortened, that backstory has given the appearance that Facebook, an ad company, is sloppy with its data. It’s allowed some people, including Tim Cook, to say that Facebook has been knowingly selling its users’ data abroad. That’s factually incorrect.

The whole reason this is in the limelight right now is because some people are in search for a scapegoat that neatly explains how Donald Trump became president, and Facebook is an easy target. It’s sheer hypocrisy however, because operatives from the Obama campaign made exactly the same use of this Facebook data, whilst the gatekeepers of media heralded it as a forward thinking use of social media. It’s a double standard.

That’s a high level tl;dr of this explosive episode of Exponent. It provides clarity to an issue that’s caused my eyes to glaze heretofore. I highly recommend listening to this in its entirety.

Amazon’s Under-Appreciated Philosophy

Among other things, The Everything Store is an eye-opening read into the business philosophy at Amazon. As a core value, Amazon strives to put the customer first by selling products and services as inexpensively as it possibly can. Doing so allows a few things:

  1. It frees the company from products that create such high profit margins that the company can’t venture into lower-profit markets for fear of displeasing its shareholders. The end result of a company with exorbitantly high profit margins is that eventually the company becomes risk-averse and consequently uninnovative. It might take decades, but it’s ultimately inevitable, especially as the vision of the original founders wears away with their departure.
  2. It frees the company from the competition that results from selling products with high profit margins. Lower profit margins mean other companies are less likely to be interested in hotly pursuing those markets.

Amazon is a somewhat boring company in comparison to its competitors. I’ve been listening to an audio recording of The Everything Store on my runs and commute and it’s not an exciting book, really. But in terms of customer satisfaction, Amazon is soaring at the #1 spot while Google and Apple are dropping in reputation. Meanwhile Amazon has closed the gap in market cap to most of its competitors and is arguably on a trajectory to become the most valuable company in the world.

A company whose philosophy is to charge inordinately high prices will initially succeed if its products are inordinately superior. But eventually the competition catches up, allured by the lucrativeness of the markets. Meanwhile, that company has to keep charging high prices for all new things it creates, because that’s the playbook it’s created for itself and the landscape it’s taught its shareholders to expect. It’s a fundamentally flawed, non-sustainable philosophy compared to Amazon’s. The pricing structure alienates customers and is built on the hubris that the company can achieve and — this is key here — sustain inordinate superiority in all markets. That just doesn’t hold up indefinitely. Eventually the competition arrives and nibbles at the edges. Eventually the cookie crumbles.

Jeff Bezos’ company has the high ground. This is going to become much more clear to everyone over the next decade.

That Building Looks Familiar  

A. G. Sulzberger, writing for the New York Times in 2011:

As the anniversary of Sept. 11 approaches, the energy firm that owns the building, the Williams Companies, remains so concerned about the similarity that it has tried to keep the connections secret. Even longtime employees making their way inside still take care not to let their eyes linger upward too long. Some cringe at the sound of passing planes, with one saying, “It makes your heart stop.”

I have a direct view of this building where I work. It’s just a few hundred yards away. I hadn’t realized until this week that it was designed by the same architect who built the World Trade Center. No wonder the building looks so similar.

iOS Bugs Appearing in Apple Ads  

If there was any doubt that the golden era of Apple was over, this is the nail in the coffin. The company will continue to achieve great things for some time to come much in the same way that Holywood continued to flourish subsequent to its golden era. But Apple is not the same perfectionist underdog for which its original enthusiasts were first attracted to it.

Death by Self-Driving Cars  

More people die in the U.S. from opioids (prescription painkillers) and heroin than from firearms. In 2015, nearly 4 times as many people died from drug overdoses than from guns.1 If we derive our morals from desired outcomes2 and buy into the notion that more gun control will reduce gun violence, then to be consistent, for every protest we make that there should be more gun control, we should protest 2-3 times as often in favor of outlawing painkillers.

I don’t know anybody who does that. I’m guessing you don’t either.

My point is this: it may be that humans are more dangerous behind the wheel than are computers. But I bet 20 years from now, computers will slip up and still kill pedestrians, such as in this tragic story from The Verge. Even if computers are 4x safer than humans behind the wheel, the public won’t allow it. There’s something about the computer-driven deaths that’s sickening and perceptively avoidable in ways that the human-driven ones aren’t.

  1. I’d be willing to bet that about half of Americans would guess that this ratio is roughly flipped. In doing so, they’d only be reflecting what they’re directionally led to believe by mainstream media. ↩︎
  2. As opposed to absolute morals that govern, as a matter of secondary consequence, social outcomes; which in broad strokes is the more correct approach. ↩︎

Yearly 1.2  

Yearly 1.2 is now available for download in the iTunes Store. The biggest change I made was enabling a custom start date other than January 1 for the reading schedule (a request I’d received from friends and strangers alike). For the settings panel I implemented SlideMenuControllerSwift, which was really a joy to work with.1

While I was at it, I added a few more things to the settings panel, including a handy link to go to the app’s Settings for notification management, as well as a link to iTunes for leaving a review.

One new change in 1.2 that I didn’t mention in the (admittedly scanty) release notes is this: when you download the app for the first time, instead of just blindly showing a standard alert dialog requesting that you grant permission for notifications, I first show a custom dialog box that contains this message:

Yearly works best when you receive a morning notification. It silently appears on your lock screen at 5:00 AM and shows what your reading is for the day. To enable notifications, please tap “Allow” in the next window.

Underneath this custom dialog box is a single button whose title is, “Ok, got it,” upon which tapping inside brings up the aforementioned default dialog.

All of Yearly’s UI is built in Storyboard and I’m starting to seriously wish I’d taken the time to learn how to do it programmatically instead. At this point, whenever I want to rearrange anything, the number of constraints I have to redo is tedious. Sometimes I even have to dip into the XML source code of the Storyboard to remove, say, an outlet reference to a view that’s been added, hooked up, and subsequently deleted.2

  1. I’ve learned that you can’t judge a book by its cover when it comes to 3rd party code. The documentation screenshots for SlideMenuControllerSwift were a turn-off at first, but once I realized how smooth, reliable, and flexible its underlying UX was and that its UI was completely determined by what you brought to the table, I decided to give it a go. If I can be vain for a moment, I think the design of Yearly’s settings panel is better than the screenshots for SlideMenuControllerSwift and does a better job “selling” the library. ↩︎
  2. Perhaps the bigger lesson here is that unless you’re an iOS ninja, you want to do your design prototyping elsewhere, and only go into Xcode once you have a pretty solid idea of what you want to design. It doesn’t have to be drafted elsewhere to the pixel per se, but if you’ve got the basic building blocks nailed down before you start implementing them, you’ll save a lot of time. The pitfall I often run into is that I have exactly what I want to build in my head and think it’s all a solved problem, but then when I go to implement it, I’m forced to reckon with scenarios (relating to both UI and UX) that just don’t fly in real life. ↩︎

3 Days a Juror

Early February I received a summons in the mail to serve as a juror for the District Court of Tulsa County. My start date was March 5. This is a recounting of the three days in which I served.1

I arrived at the Civic Center Parkade parking garage in downtown Tulsa on Monday, March 5 at 8:40 AM, blaming the 10 minute tardiness to the backed up traffic at a dysfunctional light at 96th and Riverside. At the parkade, I asked the lady if parking would be free for me since I summoned for jury. She maintained that it would still cost me $5, which meant I’d be making $15 that day, not the $20 we were promised as reimbursement for our service.2

The car safely parked on the second floor of the garage, I stumbled about on foot downtown, unfamiliar with the area. I finally arrived at the courthouse at 8:50 AM, now a solid 20 minutes late. I was exhausted and weak with hunger, having run a 10K that morning with no breakfast. There was a long line outside the Tulsa County Courthouse thanks to the rigamarole of going through security. It was a crisp and rather windy 50°F, and people were wishing aloud that they’d brought coats as they shivered in line. The security setup was similar to an airport’s. When it was finally my turn to put my backpack through the scanner, an agent flagged it for containing a fork utensil, an item the courthouse had recently begun banning. My fork was intended for some grilled eggs which would serve as my breakfast once I had a moment to myself. The agent made it clear that he couldn’t throw away the fork himself; it was my responsibility to dispose of it outside the courthouse, which meant exiting and starting over. I walked the four blocks back to my car and wolfed down my breakfast, using my precious fork. Then I left the fork in the car, walked back to the courthouse, and started all over again at the back of the line.

By 9:40 AM I was finally in the courthouse basement, which serves as a waiting room for summoned jury candidates. There were hundreds of people down there, as well as a free wifi hotspot that couldn’t handle the load. I tried tethering from my smartphone but its connection was E (extended) down there in the concrete bunker. In the back of my mind I planned to arrive at 8:00 AM the next morning to beat the crowds, but I learned a lady next to me had arrived at that exact hour and been surprised to see the line already fully formed. It made zero difference apparently.

Around 10:30 AM we were given a 10-minute break and reminded that if we left the building, we would have to go through security again. I went to a vending machine, still faint with hunger, eager to get some chips or most anything. The vending machines only accepted cash, and I only had credit cards. There was an ATM next to it but ATM’s don’t accept credit cards. Luckily there was a little shop open next to the vending machines that accepted credit cards. I got a mediocre hot dog with cheese for $2.50. The shop was so slow and overwhelmed with jurors that I arrived back to the basement 5 minutes late, but lots of other people were still coming from break too, and nobody cared.

At 11:45 AM we were given a 90-minute lunch break and reminded that if we took our cars anywhere to eat, we’d have to re-enter the parking garage and pay an additional $5. As the room cleared out, I noticed that the wifi started working as its load went to manageable levels. I had to go eat lunch myself though, so after a few minutes of computer work on a side project, I walked the 4 blocks back to my car again. I’d begun noticing that the heels of my feet were hurting from a new pair of dress shoes. When I got back to the car, I realized that my also-brand new socks were wearing down right where the top back of the shoe met the heel, and they were soaked in blood. Worried, I took off my shoes and socks and discovered that my heels were rubbed raw. All of that walking had caused a real ruckus. After finishing my sandwich I limped back to courthouse basement, wincing with every step, trying to make it look like no big deal.

By 2:00 PM, stuck in a chair with white noise reminiscent of an airport terminal, with wifi intermittently usable, I had grown suicidal. I started to wonder if maybe the best course was to just hang all the defendants regardless of their status and release all of us prisoners from the basement. It was dreadful down there. A lot of people were on their phones, some were trying to nap, others were putting together jigsaw puzzles. It was crowded and difficult to get work done as we watched our lives tick away.

But then at 2:30 PM, a bailiff arrived and summoned a group of us into a courtroom for a criminal case. It involved a man accused of pointing a gun at someone else back in 2016. The judge excited us by promising that the trial would be over by end of tomorrow, or Wednesday morning by the very latest. She asked us some preliminary questions, a process called voir dire. The goal was to weed out from the pool of potential jurors anyone who might not be able to offer a fair assessment due to conflicts of interest, biases in favor of one party or the other, etc. One of the jurors recognized the defense attorney as someone who had defended his brother in a previous case, and this juror was consequently summoned to return to the basement as an illegible candidate for this hearing. The judge’s preliminary questions were over by 3:30 PM. We were told to return to the basement the following day no later than 9:30 AM, at which point our bailiff would take us back to the courtroom.

The next day, Tuesday, I put on some band aids on my bloody heels, and used a different pair of shoes that unfortunately also rubbed in the exact same spot. The line through security was much shorter this time, almost nonexistent. It dawned on me that since jury summons occur on Mondays and each day thereafter people are dismissed or told to come back at varying times throughout the day, the Monday line wait is probably always the worst.

Back in the courtroom at the appointed hour, the prosecuting attorney and the defense attorney questioned us about various things, continuing the voir dire process from the day before. We were asked whether we owned guns, where we kept them, if we were members of the NRA, and if we thought there was a responsible way to handle guns. A good half of the room were gun owners. Next, the prosecuting attorney asked if anyone would have a hard time convicting someone over purely circumstantial evidence. One fellow did, and the attorney said that they would like to dismiss him (each side gets to choose a total of 3 people to dismiss). Because this dismissal process is supposed to happen while the court is adjourned, the judge indicated that the attorney’s dismissal request was premature. I’d later learn that this was the very first trial in the prosecuting attorney’s career. After the questioning was complete, we took a 15 minute break. When we returned, the jury selection was announced. It was comprised of a total of 7 people — 6 jurors and one backup, called an alternate juror. I wasn’t chosen.

I’d learn the following day that the defense attorney had been at his profession for twenty years and that the jury acquitted his client of the charge. I have no idea if the man was guilty or innocent, but sometimes the winner is determined by who’s the better attorney, not by who’s telling the truth. When talking about this with another fellow juror, a bartender who had also been dismissed, and we both agreed that, though we’d heard none of the evidence of the case, we didn’t like the vibes from the defendant and suspected him to be guilty of the charge. As an aside, imagine if we’d been selected for that jury. And then imagine that this sort of thing really does happen all the time in criminal courts. Jurors affirm that they won’t make up their minds until they’ve heard all the facts, but from the very outset, they have intuitions about how they feel about a case. Of course they do. They’re already leaning towards a verdict. Nobody is truly objective, no matter how much they want to think that they are.

Those of us who weren’t selected for the case were told to return to the basement. We anticipated we’d be dismissed, done for the week, and done for the next 5 years.3 Alas, when we descended from the 5th floor around 11:15 AM and reported at the basement desk, we were told we could take a 10 minute break and then we had to return. There were still cases for that week that didn’t yet have juries, and everyone had to stay in the pool until all of the cases had their juries selected.

One thing I started noticing was that there were three types of people down in that basement. First, there were the down-and-outers, the bums who lived on Netflix all day long, who got out of bed assuming the day was a wash anyway, and who consequently didn’t seemingly mind being there in the least. Second, there were the upper middle class elitists, who made plenty of money and were kind of bummed that they had to be there, but were taking it in stride as they fielded important phone calls, who knew they wouldn’t really feel the loss of money that they’d otherwise be making at their cushy jobs. Third, there were the average hard working middle to lower middle class country boys, who were somewhere between upset and furious that they had to be there, and eager to get out as soon as they could, who didn’t have any hesitation loudly dropping lines like, “THEY’RE GUILTY” as soon as their names were called, in hopes that they’d be dismissed for cause before their plaintiff even arrived.

At 11:45 AM we were once again given a 90-minute lunch break. “It’s not very often you get to take a ninety minute lunch break,” the lady at the desk said over the intercom, trying to give us something to be grateful for amidst the misery of the basement. I met up with a friend working a few buildings down the street. We went to a company cafeteria, got some nachos, and exchanged notes of how our weeks were going. After returning back around 1:00 PM, I opened my MacBook Pro and got back to work, doing some freelancing on a WordPress project. We were told that there was one more jury that needed to be assembled and that once that was complete, the rest of us were free to leave the courthouse for good. About 2:00 PM they started calling the names for this final panel. I listened as the names rattled off, one by one. And then they called mine.

I cringed when I heard it, knowing that this meant I’d have one more day to go at a bare minimum. A cowboy who’d been in the pool for the previous criminal trial, who had been particularly eager to leave and had gone on and on about his gun collection in hopes of getting dismissed from the case, and who’d had his wish granted, heard his name called too. As I picked up my backpack and prepared to meet the bailiff for this new case, I asked him with a rueful grin, “Why is this happening to us?” His loud, grim answer was, “I guess we just got f****** lucky.” Once we were all assembled, the bailiff told us that he had some “good news,” which was that the trial wouldn’t start until the following day at 2:00 PM. You could hear the jurors grumbling, wondering what was good about this news. As far as we were concerned, it simply meant an extension to our jail time.

The bailiff revealed that this would be a civil case, and I could tell from the number of juror candidates for this trial that the jury would be a full 12, not 6 like the previous criminal case.

Two or three people, including the cowboy, approached the bailiff and told him they had a problem. They didn’t get paid except for days they actually worked, they said, and if they lost any more days of work at their regular jobs, they would stand to lose some serious coin for the month. The bailiff replied, “I’m sorry, but there’s nothing I can do about that. You’ll have to talk to the judge about that tomorrow at two o’clock.” “At two o’clock,” repeated the cowboy with a long face. He began plotting a scheme.

Wednesday rolled around, and this time I was finally forced to discard my new dress shoes and put on an old disheveled pair instead. They were the only shoes that didn’t murder my heels with every step. I arrived at the 5th floor of the courthouse by 2:00 PM and we walked into the courtroom. The case was a lawsuit in which someone had rear ended another driver. The voir dire process would go on to expose cause for removal for three jurors before the plaintiff and defense attorneys exercised their three respective strikes.

To start with, there was a man who worked for a lawn care business. He tried to make a case that if he was not excused to go mow his clients’ lawns, his company would lose those contracts, and since he was the newest employee at the company, he’d be the first victim of the resulting layoffs that he seemed so sure of. At that point he would be unemployed, and that wasn’t groovy because he needed to put food on the table for his wife and daughters. In other words, serving as a juror would be undue hardship. He made it clear that he had expected to be done with his jury duty by mid-week. The judge tried to be sympathetic but emphatically told him that jury duty typically lasted for one week and that the balance of regular work and civic duty as juror was something to which everyone was subject. Mr. Lawn Mower wasn’t going to get off that easy.

After hearing this rant, the cowboy from the previous day offered a better excuse for himself. He said that yesterday he had gotten word that his uncle had passed away and that the funeral would be held tomorrow, Thursday. I was floored when I heard this. He hadn’t said anything about this to the plaintiff yesterday, I thought. What’s going on here? The judge told him that she was sorry for his loss, and excused him. Later in the hallway, I asked a fellow juror if he believed the cowboy’s story. He laughed and said it seemed suspicious. It was the perfectly concocted story. The cowboy had chosen the death of an uncle — a family relation that’s not too far removed to be deemed trivial, but not too close to raise eyebrows. It was the perfect sweet spot; too sweet. What’s the judge to do, heartlessly demand a death certificate as proof?

Maybe the story was true; I’ll never know for sure. But the previous day, this very cowboy had been complaining about how he needed to get back to earning real money, and then, just like that, sometime after Tuesday at 3:30 PM, his uncle had conveniently given up the ghost, a story he offered only after it was clear from Mr. Lawn Mower’s unsuccessful rant that a work-related concern wasn’t a sufficiently undue hardship for which to be excused from jury.

And then there was the hispanic gentleman who stated he had been on the faulty end of a car collision and that as a result of this experience, it would be impossible for him to be fair in the trial, and that he was therefore unqualified to serve as a juror in the case. I’m still not sure if he was using this purely as an excuse to be done with jury for the week or if he was genuinely being that impossible about it. I’m leaning towards the former explanation. Like all of us at that point, he just wanted to be done. We all knew that this was the final case for the week, so being dismissed from this particular courtroom meant being dismissed from the courthouse. In response, the plaintiff attorney (the one suing for damages to the defendant for his client’s sustained ongoing injuries, allegedly from this long-ago car collision) requested that the judge dismiss the juror for cause. The judge glanced at the defense attorney, who motioned that he was in agreement. And that was the end of that juror. He picked up his things and walked out.

After the judge asked some questions and we took a 15-minute break, the attorneys began their questioning. The plaintiff attorney had the burden of proof, so he started first. One of the first things he did was address Mr. Lawn Mower with his southern drawl. “I’m really getting the feeling that you don’t want to be here,” he said as kindly as that sentence can be said. Mr. Lawn Mower sighed, “I think everybody here has that feeling.”4 “Are you able to give this case your full attention over the next couple of days, or is your mind constantly going to be drifting to your work?” “I think about my work all the time. Especially so when I’m at a place like… this.” The plaintiff attorney knew not spend one of his three strikes on someone who could be dismissed for cause. At the consent of the judge and the defense attorney, he relieved Mr. Lawn Mower of his juror duty, and the latter shuffled out, clearly grateful that someone had finally freed him from the courthouse cage, albeit grieved that it had taken this long. As he walked out, a sense of relief swept through the whole room. You could feel it.

After the attorneys finished their voir dire, we took a 15 minute break whilst they selected the final jury. In the hallway, I chatted with a fellow juror who was a bartender — the same aforementioned bartender — about what our chances were of getting selected. Both of us were hoping we wouldn’t get selected so we could go home and be done with it, but we wanted to go about it more classily than those three who had been dismissed. After all, it would be downright embarrassing to have to explain to your family and friends that you were dismissed for cause.

The bailiff summoned us back in the courtroom a few minutes shy of 5:00 PM, and the judge announced the jury. As she called out the names, neither Mr. Bartender nor I were selected. But there was one little problem when she was finished: there were only twelve people in the jury seats. The alternate juror had not been announced. An attorney pointed this out to the judge and they huddled around the desk, comparing notes to see who the 13th person was. Finally they figured it out, and alas, it was Mr. Bartender. He walked to his juror seat, sat down, and looked back at me, grinning sheepishly. I grinned back. He would have a very long Thursday and Friday. This was going to be a boring lawsuit for him; since it was a civil case and not a criminal one, only 9 out of the 12 jurors would have to come to an agreement, but because he was the alternate juror, he likely wouldn’t even get to contribute to that verdict.

All I knew was that it was 5:00 PM, I’d served 3 days as juror, been in two courtrooms for two cases, been selected for neither one, and it was time to go home. Those of us who had not been selected walked out of the courtroom and celebrated in the hallway that we had not been chosen. We were free.

I went in search of an elevator.

  1. Drinking Caffeine is a technology-focused column but I’m realizing that vivid, unique tales are also interesting and hard to pass up. Also, for full clarity, the trials in which I was made privy have been completed. Everything in this story is non-sensitive information. Still, you’ll notice I’m vague with the names of pretty much everybody in here. That’s attributable to the simple fact that I’m terrible with names. ↩︎
  2. I would later learn that at some earlier point, the court had been tasked to decide between either providing free parking or reimbursing for milage, and it’d opted for the latter. I also learned that mileage reimbursement is calculated automatically based on your documented address of residence. ↩︎
  3. If you’re summoned to jury any time during the next five years after a previous summons, you are excused. ↩︎
  4. His answer here was vague. I’m not sure if he meant, “Everyone here has the feeling that I don’t want to be here,” or, “Everyone here has the feeling that they themselves don’t want to be here.” At first I thought he meant the former, but upon reflection I think he meant the latter. ↩︎

What Good Is Your Mac Doing You?

When a user visits your web application, is it clear to them that you must’ve designed it on a Mac?

We say we use Macs because we deeply care about the user experience, but then all to often, we go and use those Macs to design web applications with UI / UX that could’ve been achieved using a Windows machine.

There are people out there right now building better websites using Windows than many with macOS. Developing on a Mac is not a guarantee that you’ll build something great. Your app’s polish is dependent first and foremost on your hand, not your paint brush. If you’re determined, achieving greatness apart from the Mac is within grasp.

Don’t hide behind the excuse that you can’t get your work done on anything other than macOS. Windows 10 has bash support, VSCode, Chrome, Slack, and iTunes. Most web developers don’t use any apps or software that are actually unique to the Mac.

If your Mac isn’t making a material difference in the quality of your output, you need to stop and ask yourself who you’re kidding. If your app is just as mediocre as everyone else’s, then you simply paid an awful lot of money for a pretty piece of aluminum. There’s one and only one person who’s benefiting from that aluminum.

Update 4-30-18: Maybe, just maybe, your Mac is allowing you to keep your sanity.  

Nathan Kontny, CTO at Highrise, writing at Medium:

So I made Trick A Journalist as satire on how bad marketing has gotten. But I also made it for a much more important reason.

It’s a Bad Marketing Honeypot. The people signing up for this, and unfortunately there’s quite a few, are banned from using Highrise.

In Shoe Dog nomenclature, I imagine that Phil Knight’s dad would call building “jackassing around” on the web, but still, this is awesome.

Calculate Your Average Interval Pace and Total Interval Distance for a Strava Run

Lately I’ve been implementing Jack Daniels’ Running Formula step-count interval workout. The way it works is this: you run at interval pace for 20 steps, then you jog for 20 steps. Then you run at interval pace 40 steps, then you jog 40 steps. You keep doing this until you do 200 steps of each pace, then you run another 200 of each, and wind your way back down: 180 interval, 180 jog, 160 interval, 160 jog, so on and so forth until you arrive once again at 20 of each.1 This run comes out to be between 3 and 4 miles in total distance and about 2 miles of it is at interval pace. If you’re running 20-30 miles per week, this is a good amount of interval running for that week.2

Once your activity is uploaded to Strava, you’ll see a zipper pattern of a fast lap, a slow lap, a fast lap, a slow lap. Lurking amongst this data are two things that Strava doesn’t reveal out of the box: the total distance of your interval-pace laps, and this total distance’s average pace.3 Everything you need to compute these two data points exists; you just need a way to harvest it. This is where Strava’s API comes in.

First you’ll need an access token. To do this, create a Strava app4 and then head to this URL:[clientID]&response_type=code&redirect_uri=http://localhost&scope=view_private

Change out the [clientID] with your app’s client ID.

Once you’ve authorized your own app, you’ll get redirected to localhost, which unless you have a local server running will result in a “This site can’t be reached” Chrome page.5 That’s ok though, because your payload is the code parameter in the URL. Grab that and do a POST request to with these params:

  • client_id
  • client_secret

I like to handle POST requests like these using the Advanced REST client. If you’re using that for this, you’ll want to select multipart/form-data as your body content type in order to get this to work.

This POST will return a payload with your athlete information, but the piece you’re after is the access_token. Store this somewhere safe. You worked hard for it, it won’t expire, and you’ll reuse it later.

Now, get the ID of the activity you’re interested in. You’ll find the ID in the URL of the activity. E.g. has an ID of 1428717700. Now go here to get the payload of your activity:6[activityID]?access_token=[yourAccessToken]

Copy all the JSON from the response of this request, pop open your Chrome console, and write this:

const activity = [paste from clipboard]

Then run this in the console:

const totals = { distance: 0, seconds: 0 };
activity.laps.forEach(lap => {
  if (lap.pace_zone === 6) {
    totals.distance += lap.distance;
    totals.seconds += lap.elapsed_time;

totals.distance =  totals.distance * 0.000621371;


You now have, logged at the bottom of the console, the total number of miles and seconds of your interval pace. Head over to (or similar — there are a number of online calculators that achieve what that one does, so use your favorite) and plug in these numbers, and you’ll get your output. This is the process by which I can know that my run today contained a total of 2.15 miles at a 5:33/mi pace, even though the overall activity was 3.45 miles at a 7:46/mi pace.

Groovy, right?7

  1. I don’t keep track of my footfalls in my head, although you could do that if you had to. Instead, I manually lap each set of steps using my Garmin vívoactive 3, which conveniently lets you see your number of steps per lap and lets you manually create new laps. As an aside, it’s exactly this sort of functionality that Garmin offers and that Apple Watch doesn’t that explains why serious athletes still prefer Garmin. ↩︎
  2. Jack Daniels recommends that your weekly amount of interval distance be the lesser of 10K or 8% of your weekly mileage. This is right in that sweet spot. ↩︎
  3. These two data points are immensely useful, because they let you know how you’re performing compared to previous workouts. Your total distance and total average pace aren’t as helpful for this sort of workout because those are influenced heavily by how quickly or slowly you’re jogging in between interval laps. The question we’re trying to answer is this: how much quality interval time did we get? How fast were the intervals and how much distance did they cover? In my mind, the answers to those questions are some of the most valuable things we can know when analyzing a completed interval workout. ↩︎
  4. This app is for your eyes only, so name it whatever you want, and upload a picture of your dog as the app’s icon. As an aside, it’s bizarre to me that an icon is required when creating a Strava app. Why? ↩︎
  5. I assume you’re using Chrome for all this. 😛 ↩︎
  6. Strava access tokens are per-athlete which means you’ll only be able to do this on your own activities. ↩︎
  7. If you’re thinking, “This is quite a hassle and this entire process should be automated in software,” you’re on the right track. I eventually want to build this out similarly to how I built out, but it all takes time, and right now I’ve got too many other irons in the fire. So for now, computing this number takes some elbow grease. Our only consolation is that the coaches and runners who lived pre-Strava and pre-Garmin must’ve done an awful lot of number crunching by hand on yellow notepads. I don’t envy those days. Update 6/16/2018: I’ve now built this out so you don’t have to compute all of this by hand. ↩︎

The Stack Overflow Question That Led to Ross Ulbricht’s Arrest  

Moments after posting this question, Ross Ulbricht changed his email address on Stack Overflow from one in which his name was associated to the fictitious “[email protected]” Stack Overflow keeps track of all previous email addresses used, and when an IRS agent approached Stack Overflow with a subpoena, the company provided the original email address, thereby revealing the identity of the user.

Ross Ulbricht is in jail for life for masterminding the Silk Road website. This Stack Overflow post was a critical clue.1

  1. I highly recommend reading American Kingpin for the full story. ↩︎

Michael Lopp’s iPhone Home Screen  


Strava. Because smaller more connected [villages] are bringing us a healthier internet. I have a lot to say about Strava in a future article. I pay a subscription fee for Strava.

It warms my heart that the Vice President of engineering at Slack has Strava on his home screen.