Staying Creative

There's a truism in tech that goes something like this: founder-led companies outperform, while companies with a hired CEO eventually struggle. You hear talk about "missionaries" vs "mercenaries".

There's a second, closely related truism that startups are nimble and bold, and beat entrenched incumbents by simply moving faster. Eventually, past some level of size and success, they become entrenched incumbents themselves and start to suck. They keep hiring, using more employees and more resources to deliver less visible progress. The classic distillation is Bezos' 2016 Day Two letter

Ebay employs 13,000 people, and their product has barely changed in 20 years. Screenshot from Jan 2022.

Why, though? What pulls a company toward mediocrity as it grows? Why does that process seem to speed up once you have outside leadership?

Equity behaves in weird ways as an incentive. In the early stages, it's a simple pass/fail: everyone's equity is either worth nothing or a lot. This is excellent for alignment. It means that everyone's priority is making the company survive.

Then past a certain size, new executive hires are chasing increasingly abstract metrics. Say you join a successful SaaS business and get an option grant. A lot of future growth is already baked into the current share price, which is the strike price for your options. Now say that the best thing for the business is to focus on quality, improve customer service, and grow a little slower. As the exec it's hard to make that call, because in the short term lower growth = lower price = your options are worthless. Rather than a pass/fail test, new execs are now taking levered bets on marginal growth relative to previous expectations.

Meanwhile, new individual contributors don't have meaningful equity at all. This can lead to a double alignment problem. On the ground, you have people who don't care that much--it's just a job to them. On top, you have people who care a lot, but not about the business per se. They care about small movements in specific metrics against artificial targets.

This is how day 1 becomes day 2. This is why founders outperform: they're incentive aligned in a way that's nearly impossible to achieve for hired leadership.

The best companies seem to do two things to avoid Day 2. The first is careful incentive design, and a culture of high expectations. Netflix has that famous line about how "adequate performance gets a generous severance." You have to actively select against people who don't care very much, because by default they'll eventually become a majority. Harsh but true.

The second, and even more important, is to give people something worth caring about. More than money, the best technologists want actualization. People are starved for meaning. This is why the doomer memes about "the best minds of my generation are optimizing ads" resonate. Everyone wants to do something that they can be proud of. That's why Apple will never have trouble hiring the best designers, even if someone else is paying more.

The very best companies represent an idea, and their founders are storytellers.

Keep it simple, stupid

Project Xanadu was the very earliest prototype of the world wide web. You could call it web0. Work started in 1960, a decade before ARPANET.

The creator is a fascinating, Captain Ahab-like character who to this day insists that Xanadu got it right, and that the web as we know it is flawed. Throughout the project's 60 years, he's insisted on unbreakable links. Hyperlinks in Xanadu go both ways: if you can jump from page A to B, you can also jump from B back to A. And they maintain "referential integrity", meaning that there's no such thing as a broken link.

The real web, meanwhile, started out dumb and simple. Anyone can publish HTML. You write <h1>hi</h1> in Notepad. The HTML can link anywhere. If someone clicks a link, then the browser will make a best effort to load that page, giving up if it's no longer there.

And it's perfect.

The lack of requirements isn't just an expedience. The zen of the web is permissionless publishing, free of charge. There's a straightforward line from that to why unbreakable links are impossible. If I can make a 1 petabyte file and link to it for free, then I can't also force the rest of the web to somehow keep hosting that file for me forever. Once I stop hosting it, the link breaks.

So the real web isn't a failed approximation of Xanadu. It works because it has fewer requirements. Less is more. Less is correct.

Many such cases.


The core problem in software is complexity. Software lets us build sandcastles as high as we want, and we do, to everyone's detriment. No other field of engineering comes close. Building a car with 100,000 moving parts would be nearly impossible and clearly bad. But your thermostat runs node.js.

Over and over, failure in software comes from excess complexity, and excess complexity comes from requirements that aren't real. Nerds love requirements.

Great software does less, but better.

The best software does one thing perfectly.

Losing time

There might be a new kind of MEV emerging that will incentivize Ethereum miners and validators to mess with the block timestamp. As far as I can tell, this could cause the on-chain recorded time to fall further and further behind real time.


Timekeeping is a tough problem in distributed systems, one that's deeply related to the problem of achieving consensus. One way of looking at blockchains is that they are simply sophisticated timekeepers. Satoshi's big invention was a fully decentralized way to agree, for any two transactions, which one comes first. Every other ingredient for Bitcoin--public key signatures, Merkle trees, gossip protocols, proof-of-work--had already existed for over a decade beforehand.

Bitcoin and Ethereum don't rely on wall clock time for the core problem of transaction ordering. They do, however, include a timestamp in each block. Both share a simple timestamp validity rule. For each new block:

  • The timestamp must be greater than the timestamp of the previous block
  • The timestamp can't be in the future.

That second condition is subjective. You could create a block which one node accepts and another rejects, because its clock is a little behind. So far this has not been a problem: a block proposer cannot put the timestamp far in the future, or the rest of the network won't accept it and it'll be as if they'd proposed a block at all. If they put a timestamp, say, one second in the future, then the rest of the network accepts it in waves over the coming second or two --just a slightly delayed version of normal block gossip. They can't set the timestamp far in the past, due to the greater-than-previous-block rule. Historically, there's been no reason for them deviate from the default client behavior of just using the system time. This results in chains with mostly accurate block timestamps.

Ethereum contracts reading the wall time

Historically, nothing important on chain depends on the block timestamp. Scheduled events, like hard forks, soft forks or NFT drops, lockup periods for exiting a rollup, etc etc are all done in terms of block height. Block height is the total number of blocks since genesis, and can be used to time future events approximately. For example, the next Eth fork is scheduled for block #13773000, expected on Dec 9 around 12pm Pacific. Block height is impossible to game, short of directly attacking the chain via a fork. A block proposer (= a miner today, validator soon, maybe a specialized proposer role after PBS) has no control over block height. Any valid block is simply #(previous block height + 1).

An ethereum contract can read the block height. Ethereum also allows a contract transaction to read the timestamp of the current block. (It's impossible to allow a transaction to read the hash of the current block. The reason why is left as an exercise. 😇)

What's new

Some contracts are using the current block timestamp as a randomness mechanism. That's because it's the only piece information that a transaction can read that's typically unknown to the sender of the transaction. Check out this very interesting Snake game implemented entirely on chain.

ArcadeGlyphs NFT contract

Underlying snake game contract

Specifically, check out the insertCoin function in the Snake contract. It records the previous block hash, the current block timestamp, and current block height. These serve as the random seed for each game.

Only 2000 Snake NFTs can be minted. The top-three scoring games get special additional 1-of-1 prize NFTs. If I'm a block proposer, I can bend the rules in my favor by picking just the right block timestamp.

You can see where I'm going with this...

Flashbots for timestamps

The Ethereum ecosystem has developed out-of-band block space auctions. The most important one is the Flashbots protocol. There are others, and the details are fascinating, but that's for another post. The bottom line: it's already standard practice for some people to pay block proposers to do things their way. Today, they mostly say "here's are some private transactions--don't let anyone see them, but include them at the very front of the next block you mine". 

If any application using block timestamps for randomness gains enough value, the same mechanism will be extended to bid for a particular block time. What would this mean for the network?

Timestamps are Unix time in seconds, so initially bidders would have just a few possible values to choose from--anything between previous block time and current UTC time, which on Ethereum is usually anywhere from 0 to 30 seconds later.

MEV searchers would bid for times as part of a flashbots bundle that includes a transaction taking advantage of that particular, favorable timestamp. Their bots would iterate over the range of allowable timestamps, simulating the contract "randomness" at each to find the one most favorable to them.

Since this is being used as a random seed, the "best" timestamp will be uniformly distributed in the range of allowed times. On average, the auction-winning timestamp will be halfway between the previous block time and the current time.

How much will blocks end up delayed? I ran a simulation to find out.

Looks like with every block proposer auctioning off the block timestamp, we end up on average 14 seconds = exactly one block interval delayed. On closer thought, this makes sense! We can see that this is a stable arrangement:

Call the mean time between blocks t = 14 seconds. The last block was t seconds ago, so its timestamp is 2t seconds ago. A new block arrives. The auction-winning time is randomly distributed in the range [now - 2t, now], which is on average again t seconds ago.

Tldr; once we have timestamp auction, newly found block timestamps go from (present time) to (roughly the time of the previous block). Specifically, they become: present time minus a randomly distributed 0 to ~2x the block time, with outliers. This is assuming contracts are using the current timestamp as a random seed, like that Snake game. If an app ever manages to expose significant value for earlier timestamps, then the block times could fall arbitrarily far behind real time. 

What can we do about it?

One way of nipping this in the bud would be to redefine the EVM TIMESTAMP opcode (and by extension block.timestamp in Solidity) to refer to the previous block timestamp. This would have to be done in a future hard fork.

This would restore the original economics, where there was no reason for a proposer to pick a fake timestamp.

The Snake game would still be gameable. Eventually, a lucky block comes around where the next call to mint() will return a winning snake game. But it will be deterministic. Players simply bid for who gets to be the next one to call mint() first. That's regular old MEV, the kind we already have lots of.


It looks like there's already a Github issue in Flashbots for adding timestamp auctions:

Light on details, so I'm not sure if we're talking about the same thing. I'm also not sure if anything came of it, or if any miner is already using timestamps to capture MEV today. If anyone knows more I'd love to hear it.


Gamestop Retrospective

There have been 1 billion GME takes so far, a number that will only continue to go up. 🚀🚀🚀

Some tried to tell a poignant David-vs-Goliath story, a song of class struggle. Matt Levine gave us his signature aloof, dry wit. A securities lawyer on Reddit issued a well timed warning that the party was over. Scott Galloway did his thing. He appeared on the television and immediately ate shit, making predictions that proved 180 degrees wrong later that same week.

Anyway, here's #1,000,000,001.

The thing that amazes me about this Gamestop pump is that they've managed to roll basically every powerful human motivation into it.

They tapped tribalism. People were cheering for their team. "Apes together strong." They made their own names, their own ridiculous demonyms ("autists", "retards"!), their own symbols, their own heroes. For a team that came together a few weeks ago and that anyone could join, they were impressively cohesive.

They channeled righteous anger. They weren't just taking advantage of the hedge funds' ill-considered risks for personal gain. They were defeating corrupt, evil entities, the same ones that gave us the 2008 crash(*).

* Not really the same ones. But memes aren't about precision, and "hedge funds bad" is a valid take, overall.

They even reproduced the visceral cacophony of a worked-up crowd, 1000+ people dialing in from their covid basements simultaneously on Discord.

They tapped into populism, that deep tradition of power-to-the-little-guy.

They tapped into nostalgia. Gamestop was that store at the mall, back when there was a mall, back when you were a kid, back when you went there with your birthday money and bought Modern Warfare 2 and a pack of Twizzlers on the way out.

And of course, they tapped greed. Every pump, every moon, every Ponzi-adjacent thing is built on that electrifying lust, that flash-dream of retiring early and vrooming by your ex's place in a 911. Most leave it at that. It's a powerful enough motivation on its own.

But GME was so much more than that. I hope most of those retards come out of it with a good story to tell.

Going offline

1 minute read

I am going to be off screens for two months. No laptop, no social media till 2021.

I have some books, a drawing pad, and one of these bad boys for writing.

I'll still post photos here, on occasion. Here are two of my tiny little 80 pound husky ❤️

He's a new soul, born in January. 2020 has been a great year for him.

Like many people, I've spent a most of my day in front of a screen. I want to experience the opposite of that.

First thing in the morning, there's the small screen. You grab it to turn off the alarm. It tells you you've got mail. The small screen is always there. Sometimes I used to go to the bathroom without it. Then I'd grab my pockets. Where did it go?

Then there's the bigger screen. Slack, Gmail, Github all have notifications. I try to get to Inbox Zero in the morning, and then again at the end of the day. In between, whenever a tab has the unread bubble on it, it's hard not to click.

Lastly, after work, there's the biggest screen. That one is not really my vice. I find Twitter more addictive than TV. But the average Netflix subscriber watches two hours a day. The hearts of Netflix and Youtube are their recommendation algorithms. There is always a next thing to watch.

Our tech today puts us in a reactive posture. Things bubble up, are presented to us, and we respond.

Steve Jobs said that a computer was a bicycle for the mind. Well, the cool thing about bicycles is that you can ride them wherever you want. Too many folks are stuck riding a subway instead: back and forth on a set track, and every few minutes it goes ding.

In Go they call it sente. Whoever has it is starting things. The other player is responding. Sente is the initiative. It's fluid, it passes back and forth.

We have to regain sente.

Make some time. Go offline and enjoy the sunshine ☀️

Calm technology

There are two kinds of companies, those that work to try to charge more and those that work to charge less.
- Jeff Bezos

The same is true for attention. There are technologies made to capture as much of your attention as possible, and others designed to stay out of your way.

The first category includes feed apps (Facebook, Twitter, Insta) with infinite scroll; video apps with autoplay; and apps with too many notifications. The second category includes utilities like Venmo, Waze, and Shazam, and apps like Five Minute Journal.

Which kind are you working on?

In the early days at Google, they had a beautiful KPI. They tracked time spent on the goal was to make it as short as possible! The better the search algorithm, the simpler and faster the page, the quicker people would find what they were looking for.

This is the opposite of the Average Session Time metric many apps use today, where you're trying to keep people "engaged" for as long as possible.

We need calm technology. Perfection is achieved not when there is nothing left to add, but when there is nothing left to remove.

The Distraction Industrial Complex

People are spending huge amounts of time scrolling through feeds.

I'm not talking about chat apps like Snapchat or channel apps like Twitch. Those are cool.

I'm talking about algorithmic feeds of posts that scroll forever off the bottom of the page.

Posts with Upvotes or Hearts or Likes. Posts with Retweets or Shares or Revines or Reblogs.

The biggest offenders are Twitter and Facebook.

Feeds suck. 

When was the last time you scrolled thru a feed and felt refreshed and invigorated?

Felt you'd learned something new and useful?

Felt that it was time well spent?

Most of the time you read a social feed it's just a quick diversion, a way to procrastinate. It will give you a few mildly funny things to laugh at 😅 and a few terrible things to get mad about 😤 and maybe a photo of someone's suntanned feet on a white beach with a clear blue ocean in Thailand so you can feel a little #fomo.

You feel kind of bad afterwards. You know you're just wasting time, but it's hard to stop. So hard, in fact, that Facebook now has a billion daily users.

How did it get this way?

1. The companies DGAF about you

The cliche is that if you're not paying, you're not the user, you're the product.

More precisely, ad impressions are the product. Every hour you spend feed scrolling creates ad impressions, which are sold to advertisers for a few cents per thousand.

Turns out, every hour you spend scrolling is sold for surprisingly cheap.

Facebook made $18b in revenue in 2015 from about a billion daily active users, each of which use the site for an average of about half an hour a day, 365 days a year.

That means that to Facebook, an hour of your time is worth just under 10 cents.

18b USD per year / 1 billion people / 365 days a year / 0.5 hours per day = 10 cents per hour

Facebook is the blue whale. You're the plankton.

(Wait, $18 billion? Ten cents per hour? Does that mean roughly 180 billion human hours were spent Facebooking in 2015? Why yes it does! By the way there are about 400,000 waking hours in a human lifetime, so that works out to about half a million lifetimes.)

2. Feed companies make their feeds as compelling and distracting and addicting as possible

They're constantly experimenting, tweaking their product. Anything that makes you waste even more time per day, ships. This is called Driving User Engagement.

After years of optimization, they've come up with some pretty powerful ways of keeping people Engaged.

The top nav bar always hovers over your feed as you scroll, showing bright red notification bubbles, begging for clicks. The smallest things trigger new notifications. Someone you met once at a party three years ago clicked Like on a picture of your butt while you were squatting on #LegDay. Ding!

By default, everything buzzes your phone.

The feed suggests new friends. The feed suggests reposting years-old content ("Memories"). The feed suggests Liking things that your Friends Liked. The feed suggests putting a french flag in front of your profile pic.

They've reduced the effort to participate down to a single tap. Just say Yes.

All this extremely low effort content floods out to your 1000 or so closest friends' feeds. By default, almost every action you take is public.

Infinite scrolling feeds start at the very top every time you open the app. There's no way to pick up where you left off last time and efficiently catch up. Instead, you just have to scroll down and down until you notice posts you've already seen before. This is by design!

Reposts--retweets, shares, etc--and non-chronological feed algorithms mean that new and old posts are interleaved. You can scroll for a long time and never really know when you're "done". While you scroll, the bright red notification bubble lights up again because there are now "9+" new posts since you started reading. Clicking sends you all the way back up to the top.

I'm sure they A/B tested this and found that it makes the Average Session Time go up.

Such User Engagement. Wow.

3. Feeds reward the wrong stuff

Sexy photos. Baby photos. Beer drinking photos. Happy Birthday posts. Political screeds. Thoughts and prayers.

Feeds are random. They have no coherent theme. That makes them unsatisfying.

If you watch thirty minutes of Netflix, it's not exactly productive, but you do get the satisfaction of a coherent story with a beginning, middle, and end.

Thirty minutes of feed-scrolling is neither productive nor particularly satisfying. It's a random stream of bite-size miscellaneous posts.

It's just there, always in your pocket, always a finger flick away. It can be consumed anytime, anywhere, in bed, standing in line, sitting on the toilet, at lunch, or in a boring meeting. Maybe that's why 85% of video on Facebook is viewed with the sound turned off.

So the content is low effort and the consumption is low effort, too.

How do we fix it?

Well, ideally we'd nuke the Distraction Industrial Complex from orbit and build a better way of hanging out over the internet.

A new way that prioritizes quality over quantity. 

A new way that respects our attention and values our time.

If we can dream, maybe our new way will even be free from centralized control.

Until we can make that happen...

Here are a few simple fixes that worked for me

Uninstall the apps from your phone. 

You can keep Messenger, but get rid of Facebook and Twitter. At very least turn off the notifications. There's nothing healthy about having your pants buzz every time some dude from middle school Wants To Play Candy Crush Saga With You

Take a break from Twitter. 

If Sam Altman can quit, so can you.

It helps to block from your hosts file.

Update: if you want to keep Twitter but avoid distraction, check out this new Chrome extension, Kill Tweet Stream. Nate Goldman made it after reading this post (!) and he's the boss.

Install Kill News Feed.

That way, you can keep your Facebook and use it for events and chat, but without getting sucked down the rabbit hole of "news".

Install uBlock Origin.

The faster we can get to 100% ad-blocker adoption, the faster this business model of wasting billions of hours of people's time for ten cents per hour will die.


Kill the feeds and enjoy the sunshine!

How To Make A Great Electron App

Electron is excellent.

There's a long history of ways to package HTML and Javascript into an installed desktop app. The result usually feels like a web app detached from the rest of the OS.

Electron makes it easy to do better.

Electron exposes lots of deep OS integrations thru simple Javascript APIs, so you can have a single clean codebase instead of having to code against three different C++ and Objective C libraries for Windows, Linux, and Mac.

Using npm and electron-prebuilt, you can also keep your build simple and clean. No node-gyp, no native compilation at all. Things that are a pain in most environments, like installers and automatic updates for multiple platforms, are easy here.

Feross and I used Electron to make WebTorrent Desktop recently. We were surprised by Electron's quality and attention to detail.

Here's a list of things you can do to make your Electron app feel native and pro.

(If you're new to Electron, check out the Quick Start. First things first! This post is for people who already know Electron, but want to make their apps even better.)

The List

  • Dock and tray integration
  • Notifications
  • Menus
  • Shortcuts
  • Drag and drop
  • Crash reporting
  • Signed installers for all three platforms
  • Automatic updaters for Mac and Windows
  • Fast startup
  • One-step build

WebTorrent Desktop implements 10 / 10.

How does your app score?

Dock and tray integration

On Windows and Linux, you can minimize to tray.

(You can do it on Mac too, but you probably don't need to since Mac has the dock.)

This is great for running in the background or running automatically on system startup.

If you're making a decentralized app, you probably want to do this to keep your network healthy.

On a Mac, integrate with the dock.

Show a progress bar when the user might be waiting for something to finish.

Show a badge when work finishes while your app is in the background.

Caveat: only some Linux distros support the tray correctly. Check that you're on one of them--otherwise, your users will have no way to quit your program if you hide the window and your tray icon doesn't show up. See checkElectronTraySupport for a workaround.


Desktop notifications work on all three platforms. They're really easy to use.

Stay concise. Don't go over 256 characters, or your message will be truncated on Mac OS.

Here's an example with custom sounds: a satisfying "ding!" whenever a file finishes downloading.

Play sounds using the normal web audio API. You'll want to preload them. Here's a nice way to do that.


Electron gives you nice declarative menus on all three platforms.

You can use them in lots of places: context menus, dock icon menus, tray menus. Most are optional but the one you'll always want to implement is the window menu.

Follow each platform's conventions for what goes where. For example, if you have Preferences, Mac users will expect to click YourApp > Preferences while Windows users expect Window > Preferences and Linux users expect File > Preferences.

If you have a button for something, give it a menu item anyway. Two advantages: it makes your keyboard shortcuts discoverable, and it makes actions searchable under Help > Search on a Mac.

See it in action here: menu.js.


Electron supports two kinds of shortcuts: menu shortcuts and global shortcuts. 

Menu shortcuts are great. New users can click around and learn what's available. Power users can use your app very efficiently.

Follow each platform's keyboard shortcut conventions. Electron makes this easy: for example, you can specify "CmdOrCtrl+O" as the accelerator for Open, and it'll be Cmd+O on Mac and Ctrl+O on Windows and Linux.

Global shortcuts work even when your app is not focused. For example, if you're running WebTorrent Desktop in the background, playing an audiobook, while using Chrome in the foreground, you can still use the play/pause button on your keyboard (F8 on Mac) to control WebTorrent.

Drag and drop

If you want to let users drag files into your app, you'll need to handle three separate cases.

When someone drags files onto the window of your running app, you'll get the regular HTML5 drag-and-drop events.

When someone drags files onto the icon while your app is running, you'll get a special Electron on-file event.

When someone drags files onto the icon while your app is not running, the OS will run your main process with special command-line arguments. You'll have to handle those.

Crash Reporting

Electron has built-in Crashpad support so that you can get a report when a process crashes.

You might also want to be notified of uncaught Javascript exceptions. You can do this:

  • In the main process with process.on('uncaughtException')
  • In the renderer process using window.onerror

Your server will need an API endpoint to save the crash reports. Check out the WebTorrent website code for an example of how to make one.

Signed Installers

You must sign your installers. Otherwise, you'll get a scary full-page red warning on Windows that says your app is "untrusted", and modern Macs in their stock configuration will refuse to run your app altogether.

Here's a build script that does this for Mac and for Windows.

Getting certs:

To get a Mac signing certificate, sign up for an Apple Developer account. It costs $100 a year.

To get a Windows signing certificate, we recommend Digicert. The documentation for Windows app signing is surprisingly bad. If you go with the wrong vendor, they'll ask you to mail them notarized paperwork. That makes it a slow and annoying process to get the cert. Digicert is easier: they just send you a password via Certified Mail, you go to the post office, show your ID to pick it up, and bam, you get your signing certificate.

You do not have to go thru the Mac App Store, unless you want to. If you do, your app will be sandboxed and you may have to change the UX slightly to accommodate the extra restrictions and permission prompts.

You definitely don't need the Windows App Certification Kit. WACK is wack, and also kind of obsolete.

Consider starting an organization to own your project's domain and certs. It looks a lot more legit if a user downloads your app and sees "Do you want to run this file? ... Publisher: Webtorrent LLC", than if they see "Publisher: Jim Bob". There are other advantages as well. In California, starting an LLC costs just a few hundred dollars and a few hours of time.

Keep your signing certificates safe. At a very minimum, they must never be sent via email or checked into a Github repo, even a private one. In fact, certs should never ever be online at all. Store them offline, passphrase-protected. Back them up onto a thumb drive, preferably an encrypted thumb drive, and keep it safe.

Once you get your first million users, your auto updater is basically a botnet with a million nodes. With great power comes great responsibility.

Automatic Updaters

Your app is getting better every week. Remember Flash back in the day, nagging you to Please Upgrade To The Latest Version? Don't be that guy.

Ever since Chrome popularized autoupdaters eight years ago, users have come to expect software to just continuously get better and fix bugs automatically.

Writing your own reliable auto updater is hard. Fortunately, Electron has already integrated with Squirrel, which makes it easy.

Squirrel only works on Windows and Mac.

For Linux, I recommend checking for updates as you would on the other two platforms, and simply popping up a notification if a new version is available:

Here's a bit of code that checks for updates on all three platforms: updater.js

Your server will need an API endpoint to tell the app which version is the latest. This can be really lightweight. You can offload the heavier work of hosting binaries to Github Releases.

Here's our server code for the updater API.

One-Step Build

16 years ago, a smart guy named Joel Spolsky invented the Joel Test for whether a software project has its act together.

#2 on his list: Can You Make A Build In One Step?

Yes, you can! Electron makes it pretty easy to automate your build. And you can do it without any fancy tools like Grunt or Bower.

Check out WebTorrent Desktop's build script. With one command, npm run package, we can:

  • Run the linter and tests
  • Package the app for all three platforms
  • Create signed installers for Mac and Windows*
  • Create binary deltas for the auto updater

* (Almost. Right now we still need to do the Windows code signing on a separate Windows machine, but there's a bug that should be fixed in the next few weeks that will allow us to build an entire release in a single command on a Mac.)

Fast Startup

You want your app to start quickly and smoothly. If it doesn't, it won't feel native.

Check out Spotify, for example. After clicking the dock icon, the window takes a long time to appear. Once it does, it first flashes grey, then some DOM elements appear, then the style changes, then more elements appear. Each time, it reflows, so the elements bounce around.

It feels like a web page loading over slow internet, not like a native app. (Spotify's UI is built with HTML and Javascript, but it doesn't use Electron.)

Make your app load quickly.

Step 1. Measure

Right at the start of our main process index.js, we call console.time('init')

Then, once the window (renderer process) has started and sends us an IPC message saying it's ready, we call console.timeEnd('init')

That gives us a bottom-line number to get as low as possible: the total startup time.

Step 2. Get your DOM right the first time

If you use functional reactive programming, this i easy. What you see is a function of your state object. The state object should be correct and ready to go the first time you render your DOM---otherwise, the DOM might have to change immediately and your app first renders, and the elements will jank around.

In our case, WebTorrent Desktop loads a JSON config file before the first render. This only adds a few milliseconds to our couple-hundred-millisecond startup time.

Step 3. Defer loading of big modules

We bisected using console.time() calls to find out which requires() were taking the longest, and cut our startup time almost in half by loading those lazily. They are loaded either the first time we need them or five seconds after app startup, whichever comes first.

Step 4. Colors and CSS

Make sure your window background color, which electron sends down to the OS, matches your CSS background color. Otherwise, you'll see flashes of white when the app is starting and again when you resize the window quickly.


Now we're already doing a lot better than a lot of apps. The window shows up quickly and with the correct background color, then a fraction of a second later the UI shows up.

One last improvement: by adding a CSS fade-in, the window shows up and the UI smoothly but quickly fades in, instead of popping up suddenly. Try it both ways---we think this feels better:


1. Make It Native

When on Mac, your app should look and feel like a Mac app. When on Windows, it should feel like a Windows app.

2. Make It Fast

Measure your startup speed. Keep it well under a second.

3. Keep It Simple

Your users don't care if you're using Flux and Redux and React and Bower and Grunt and Less and Coffeescript. Plain npm, plain Javascript, and plain CSS go a long way. Electron supports require() natively, so you don't need Browserify.

WebTorrent Desktop uses no preprocessors at all and no build system except npm. Spend your energy on things that give your users pleasure!

Bruce Lee said it best--

The height of cultivation always runs to simplicity. 

Art is the expression of the self. The more complicated and restricted the method, the less the opportunity for expression of one's original sense of freedom.
To me a lot of this fancy stuff is not functional.

Happy Hacking!

Thirsty for War

I just got this banner ad trying to recruit me. Sums up the general tone of the defense industry perfectly. Hubris, nationalism, and technologically sophisticated bombers.

But what do they mean by "America Wins. Again."?

You'd never know it from our movies and TV shows and political rhetoric, but we have not in fact been winning.

We lost the war in Afghanistan. After almost $1000b, 14 years, several thousand American lives, and several hundred thousand Afghan lives, Afghanistan is a failed state. The country harbors more terrorists than when we started, the Taliban is still around, and still controls significant chunks of the country.

We lost the war in Iraq. After a staggering $1700b, 13 years and counting, and several hundred thousand lives, Iraq is more dangerous than it was under Saddam Hussein and its remaining people are worse off. The country is split into three warring parts: a nearly failed state in the south, Kurdistan in the northeast, and the Islamic State in the north. 

We switched reasons. First it was weapons of mass destruction, then a nation-building exercise to replace dictatorship with democracy. Thomas Friedman even tried to justify it as a generalized collective-punishment retaliation against the Arab Muslim world for 9/11. "We could have hit Saudi Arabia. We could have hit Pakistan. We hit Iraq because we could." Charming dude.

Pick any of those justifications. They all failed. There were no WMDs. The nation-building could not possibly have gone any worse. Most of the Islamic State's heavy weaponry comes from America. It's a frustratingly familiar story. We arm "the good guys" and then the weapons "fall into the wrong hands".

George Bush declaring victory twelve years ago.

We lost the war in Libya. The story played out surprisingly similarly to Iraq. The old dictator was killed. Some politicians declared victory. The resulting stable US-friendly democracy didn't quite work out that way. Just like Iraq, Libya is now split into three warring thirds, one of which is part of the Islamic State.

Hillary Clinton declaring victory four years ago.

Anyway, Lockheed, Northrop Grumman, Raytheon, and co are doing great. Profits are record. Shareholders are happy.

It's a jingoistic and amoral industry.

These companies lobby for war, though they prefer to use words like "intervention" and "military aid". They provide lucrative jobs to lots of former generals and politicians in exchange for promoting their agenda and helping them win contracts. This is called the "revolving door"--a euphemism for salary-and-bonus-based corruption.

Even now, after all of the above, they are still pushing for deeper involvement in the Middle East, telling their shareholders it would be good for business.

Many Americans want peace. 

That doesn't mean we're isolationists or pacifists. It's good to speak softly and carry the world's biggest stick. It's cool to quietly underwrite the freedom of allies like South Korea and Japan and Taiwan.

It's not cool to be in a state of endless war. The average voter couldn't even tell you which countries we're currently bombing. (Iraq and Syria by jet, Afghanistan and Pakistan by drone, and Yemen by proxy.)

War should be a last resort and strictly for self defense. We should crack down on the corruption of the military industrial complex. We need a voice. We need leaders who are serious about peace.