Borderless protocols require borderless accountability

A dumb thing happened

Last month, a defi protocol called "Beanstalk" was attacked. Like many of its kind, Beanstalk had a governance token that lets holders vote on proposals.

The whole treasury was drained in a single transaction, as follows. First, the attack transaction takes a flash loan, borrowing Ethereum and stablecoins. Then, it buys all of the Beanstalk available on decentralized exchanges, enough to form a majority. It passes a proposal transferring the whole treasury to an external account. Finally, it swaps all those Beanstalk tokens back to the original assets and repays the flashloan.


What can we learn from this?

Well first, Beanstalk made many mistakes, the most important being missing delay. Any on-chain governance needs a delay before a proposal takes effect.

More fundamentally, coin voting is broken.

But why? A similar mechanism works fine in tradfi, where shareholders vote on corporate governance decisions.


The missing ingredient is accountability.

Traditionally, if there's a disagreement about how a company should be run, 60% of the shares can outvote the other 40% and get their way. But the company is still legally required to run for the benefit of all shareholders! The majority can't pass a proposal saying "send us the company treasury". There are many subtler ways in which they could unfairly self-deal, so there are courts backed by human common sense that enforce the rules.

So coin voting with guardrails, with accountability, kind of works. Coin voting without accountability is a disaster.


Accountability how?

Tradfi relies on the law for accountability. That's not a satisfactory solution in crypto. The magic of crypto is that it's global: there is a single tip of the chain, not one per country. It is permissionless: anyone in the world can participate. It is censorship resistant: anyone can transact as long as they can connect to the global internet and pay the transaction fee.

This means that crypto products reliant on one particular country's legal system (like USDC, for example) are a kind of half-and-half dual citizen. USDC is not really a crypto product. It's a tradfi product with on-chain transparency, which is an improvement but not a revolution.

How we achieve accountability while retaining the magic is the biggest open challenge in crypto right now. We have some fascinating experiments underway like the Optimism Collective:


Borderless governance

And not to overcook it, but this is the next chapter in something fundamental for how the world works, the history of governance. Past centuries saw the invention of the modern democratic state and the joint stock corporation. Both are accountability mechanisms that prevent any individual or colluding group from taking sole control, carefully balancing that goal with the need for decisive leadership. Both are non-global. A company, even if multinational, is always controlled by one country or another.

The first few years of crypto were marked by wild experimentation, with accountability often an afterthought. We saw 1. Corporate protocols with some on-chain transparency, like USDC. These lack credible neutrality. 2. Protocols controlled by specific unaccountable individuals, like Terra or Tron. Or, worst of all, 3. Protocols that can be captured by anyone, like Beanstalk.


Borderless accountability

But we also saw the beginnings of something new and very exciting, with protocols like Bitcoin, Ethereum and now Optimism. Each of these is built from the ground up to achieve credible neutrality. To do that, they must avoid capture by any colluding group. To avoid capture while still allowing decisions to be made and the protocol to evolve requires accountability. To achieve accountability globally, without simply relying on one or another country's legal system, requires cleverness and carefully chosen decentralization.

So in Bitcoin and Ethereum, the default answer to a proposed change is "no". The bar for "yes" involves building rough consensus among users, exchanges, and validators, all of which are internationally distributed. In Optimism, the bar for yes involves both a soulbound "citizen" vote and a more widely distributed (but also more susceptible to financial collusion) token vote.


Borderless credible neutrality

It's not widely appreciated yet, but these are some of the first, experimental attempts at true global credible neutrality.

At a time of increasing conflict, many air-we-breathe international systems are revealed to be conspicuously not neutral or even weaponized. Twitter is not neutral. China's Twitter clone, Weibo, is not neutral. SWIFT is not neutral. No fiat currency is neutral. The domain name system is at least neutral in the sense that no one country or alliance owns the system, but it's balkanized. Any individual domain name can be seized and repointed by its parent government. With ENS, this is not possible. The distinction might seem subtle, but over the next few decades I predict that the protocols that survive and prove international credible neutrality will become extremely important in ways that go beyond asset prices, profit, or loss. They're trust anchors, like islands in an increasingly stormy ocean.

Prototyping

Here's a thing that I want. A fast way host a web app, for prototyping and experiments. It has to be:

  1. Simple. One git repo containing all frontend and backend code. 
  2. Reproducible. There should be zero configuration outside the git repo, aside from the domain name you register and maybe a few environment variables for secrets like API keys. Anyone can fork the repo, get their own domain, and host their own instance of the app within 30 minutes.
  3. Automatic. Pushing to Github automatically runs tests, then updates the app.
  4. Stateful. You can use a database in your backend code.
  5. Open source. You are not locked in to a proprietary provider.
  6. Serverless. It should scale down to zero. No long-lived servers that can accumulate configuration and become snowflakes.

As far as I can tell, this doesn't exist today.

Firebase gives you 1, 2, 3, and 4, but then you're locked in. If the prototype works and you outgrow Firebase, you have to rewrite your backend. AWS sort-of works: you can use terraform with ALB+ECS+RDS to make a declarative web app with an open source database. But you don't get #6. Or you can use DynamoDB, then you get 6 but you lose 5. Either way, it's a lot of moving parts.

Netlify is sooo close. You get 1, 2, 3, 5, and 6. Absolutely crushes #1, you can buy a domain directly in Netlify and deploy a web app all in a few minutes. Netlify Functions are excellent. No database though; #4 is missing. They seem to loosely recommend FaunaDB? Which is proprietary and also has the worst query interface I have ever seen?

Whoever solves this can take my money.



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.

Background

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.


Update

It looks like there's already a Github issue in Flashbots for adding timestamp auctions: https://github.com/flashbots/mev-geth/issues/4

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.

 

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 notifications. The small screen is always there. Sometimes I'd go to the bathroom without it. Then I felt 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 core of Netflix and Youtube are their recommendation algorithms. There is always a next thing to watch. By default, it starts playing automatically.


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 google.com--and 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 twitter.com 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.


Notifications

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.


Menus

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.


Shortcuts

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:


Conclusion

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!



We Need A Better PC

My challenge: I'm trying to get a computer that doesn't suck

Like lots of people I spend about half of my waking life on my laptop, so this really matters.

However, I don't want a Mac.

Apple has great design, but they sell things that are locked down, both physically and in software. You're not supposed to open them, you're not supposed to replace parts, and if they break you're supposed to take them to your nearest "Genius Bar". Not my style. Also, Apple makes beautiful hardware, but their software is getting worse.

Whatever your opinion about Apple, we can agree that there should be a least one good alternative.


No problem, I thought, I'll get a PC

So a year ago I bought a System76 Galago UltraPro, because it's fast and sleek and it comes with Ubuntu. It's also modular and hackable, easy to take apart and put back together.

Unfortunately, it turns out System76 doesn't actually make their own stuff. The Galago is just a rebranded Taiwanese Clevo W740SU. Here's my Clevo compared to one of iFixit's Macbooks:

Like most computers that don't have a glowing white apple on the back, the Galago has questionable build quality. It's made of plastic, the screen flexes a lot, and the battery lasts three hours on a good day.


No problem, I thought, I'll get a Thinkpad

Lenovo is pretty much the only PC manufacturer that has a reputation for good industrial design and quality hardware. So I went to their website to see what I could buy.

Wow, that is some bad web design. Tons of fonts and colors. Tons of tiny text. Popups. And that's just one of their many series. They display low ratings for their own products on their own website. What.

Among all of this information overload, simple information is missing.

For example: I did my own research. For what I need, their best computer by far is the 2016 X1 Carbon, which Lenovo introduced at CES recently. It is thin and beautiful, it's solidly built, it has ten hours of battery life and a screen sharp enough that Apple would call it a Retina display. When is it coming out?

Apparently it's already available--sweet!

Except that page is deceptive, because that's actually the old X1 Carbon, with a low res screen and without the new Skylake processor. The new X1 Carbon will ship later this month according to third party news sites, so buying the old one today would be a bad deal. Intentionally or not, Lenovo's own product page tries to trick you into doing just that.

Then there's the part where Lenovo, like most PC manufacturers, bundles crapware with every computer they sell. A few months ago, in a perfect storm of malice and incompetence working together in a big corporate environment, they went one step further and decided to factory-install straight up malware. They completely broke HTTPS and left their customers insecure--all to further "monetize" you by injecting extra ads into websites you visit.

The 2016 X1 Carbon still looks really good, but after all that I would rather not give Lenovo my business.


It's downhill from there.

Lenovo comes closest to Apple in building quality hardware. Other PC manufacturers, like Acer and HTC and HP, have the same problems Lenovo has, but with worse build quality and an unfortunate penchant for injection-molded plastic. Their designs look cheap. The Microsoft Surface is well built, but that's a tablet, not a laptop. The Chromebook Pixel is good, but that's not a full PC, it's a limited system designed to run Chrome and Chrome Apps.


Conclusion: everything sucks... so far

What I want is a computer with:

  • Decent build quality
  • Decent performance and battery life
  • A decent website. It doesn't have to be an icon of web design, like apple.com. It can be simple and utilitarian, like an Amazon page. It just has to be honest and up to date. It should contain pictures, text, and a Buy button.
  • A clean OS without crapware or malware factory installed

Is that too much to ask? Make one and you can have my money!

Read next: Panopticon