inessential by Brent Simmons

December 2013

The Cassandra Version

Rene Ritchie tweeted:

It’s impossible for me to put into words how horrific the revelations about privacy violations have been this year.

If the Internet Age began in 1995 or so, then the Surveillance Age began in 2001. We didn’t feel the weight of it until 2013. But the run-up to now wasn’t just the NSA: it was also Twitter, Facebook, and Google wanting to know where you are, where you were, where you’re going — and who your friends are and what you’re thinking and talking about.

(Public/private partnerships are usually willing — see the defense and prison industries, and RSA — but whether they’re willing or not doesn’t really matter.)

* * *

If you wanted to create a modern total state you might concentrate on 1) constant war and preparation for war, 2) imprisonment of absurd numbers of young men, and 3) learning everything you can about what everybody everywhere is saying, doing, and thinking.

(For good measure you might also work to eliminate the ability of workers to organize and earn a living wage. You might block immigration reform — and demonize people from other countries — in order for corporations to retain super-cheap labor from people who can’t object. You might curtail voting rights, particularly for the less-than-privileged.)

The third thing — total surveillance — used to be difficult. Now it’s a piece of cake.

* * *

One way of looking at Google Glass: now the surveillance state can see through your eyes. Even when you blink.

* * *

The difference between the Internet Age and the Surveillance Age is that in the Internet Age you could choose what you shared. (Mostly.) Now all that is left is the illusion of choice.

I want to say that we gave it all away for cat pictures — because it feels like a mean thing to say, and thinking about this puts me in a mean mood. But that’s reductive and unfair.

Instead, we quite rightly love what the internet makes possible. We love when our calendars sync, we love having our photos in the cloud, and, most of all, we love talking with our friends.

And when I’m not writing blog posts I’m working on cloud software to sync your personal notes. I love this stuff too, same as everybody else.

If I believed that the internet has been subsumed by the surveillance state and there is no hope of turning it back, I’d have to check out of the internet. If I believed that the internet as a force for good has been entirely smothered by the fog of surveillance, I’d quit.

But I don’t believe that.

I think it’s immoral to believe that, because it means giving up when the stakes are high.

* * *

Here’s why I don’t believe it:

Fuck these guys.

I’ve spent the last ten years of my life trying to keep Google’s users safe and secure from the many diverse threats Google faces… But after spending all that time helping in my tiny way to protect Google — one of the greatest things to arise from the internet — seeing this, well, it’s just a little like coming home from War with Sauron, destroying the One Ring, only to discover the NSA is on the front porch of the Shire chopping down the Party Tree and outsourcing all the hobbit farmers with half-orcs and whips.

That was a Google engineer angry over the NSA’s interception of Google traffic.

I’m not one of those people who suggest there are technological solutions to every problem. But the internet is technology, and it can adapt to new threats by using new technology.

But at the core it’s a people problem. People have to care enough to fight back. Google’s motto, its exhortation against evil, hasn’t changed — but in my mind Google is now the Fuck these guys company.

And I love that. It gives me hope.

(Obviously this isn’t the only bright light in the fight against surveillance. You can find plenty more. And it’s imperfect, because Google itself still knows way too much. But I accept that there is and always will be messiness. One step at a time.)

* * *

Carl Sandburg:

The fog comes
on little cat feet.

It sits looking
over harbor and city
on silent haunches
and then moves on.

My hope — my expectation, even — for 2014 is that the fog starts to lift.

* * *

As much as I like using the fog metaphor, the thing about surveillance is that there is no actual fog. You can’t see it. It’s everywhere and gets in everything, and it still looks like a sunny day on the internet.

But still.

2014

waffle:

There are industries of people throwing money at other people saying they’ve found a breakthrough way of doing X for Y, where X is Instagram and Y is something that couldn’t possibly be a dishwasher because that wouldn’t make any sense whatsoever. There are companies like Everpix who try to fulfill what some other companies are promising and what we all want, but they run out of money. To the extent that they can’t survive because no one wants to pay and all the funding’s gone to Twitter-optimized coupon sites that don’t know five years in how to turn a profit, yeah, we’re screwed.

Notes and Books

I’m 45 years old and my short-term memory, which used to be very good, is now average, so I take notes frequently as I work. I take notes at the very second I think of them, because if I wait even a few seconds they’re gone.

I’d use Vesper for these notes, but I’m working on Vesper. It may be paused in the debugger or in some other state where I can’t use it to take a note. While I can and do use Vesper for everything else, I can’t use it to work on itself.

So I do what I’ve done for many years: I take notes — in cursive, because it’s fastest — on 5” x 8” legal pads. I got some new pads for Christmas, my favorite kind with the hard back. Not flimsy.

I don’t save these notebooks once full, since they’re really just aids to my short-term memory. The notes aren’t worth anything later on, so I recycle them. I even tear out pages as I go. (Yes, it’s a single-user Snapchat but for words.)

This sounds terribly low-tech, but there’s a part I like about it: it connects me to the programmer — the boy — I was 30 years ago, who sat in front of his Apple II Plus with a three-ring binder full of notes and hand-written code. It didn’t seem at all weird then to mix digital and analog, and it does seem a little weird, at least to me, these days. But I seem to find excuses.

Similarly, for a while I thought I’d buy electronic books only, with the exception that I’d buy physical copies of classics and books I want to keep. But then I remembered I can give to Goodwill genre fiction that I don’t want to keep. So why not buy physical copies of those too?

Reading books has given me so much joy in life, and reading electronic books just isn’t the same joy: it breaks the thread to all the decades of past-mes with a book in my hand. It breaks the thread to all the great readers, living and gone, in my family.

So I do read electronic books, but I prefer real books. A library has a romance that a hard drive will never have.

(Lest you think this is Luddism, I’ll remind you that I write, and use like crazy, note-taking software that runs on iPhones. And I’ve written blogging and RSS-reading apps. But I feel steadier with one foot planted in the past and one planted in the future.)

Observers and Thread Safety

A friend of mine at Xcoders told me about threading issues with NSNotificationCenter and KVO. In my own code I always post notifications on the main thread and do any KVO-triggering on the main thread (for precisely the reasons he spells out) — so it’s not an issue I’ve run into. But it’s something you should be aware of.

My friend elaborated via email. Below is his email (edited for typography and typos only):

* * *

In most ways that matter NSNotificationCenter is thread safe. You can add/remove observers from any thread and you can post notifications from any thread. You’re on the hook for the thread safety of your observer/selector or block handler, but that’s a pretty reasonable way for the API to work.

Here’s a pretty normal pattern for using notification center:

- (instancetype)init
{
  self = [super init];
  if (self)
  {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(prompt:) name:@"PromptUserToRateApp" object:];
  }
  return self;
}

- (void)prompt:(NSNotification *)note
{
  // Do something async
}

- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self];
}

and then somewhere else in your code:

[[NSNotificationCenter defaultCenter] postNotificationName:
@"PromptUserToRateApp" object:nil];

This is a perfectly safe thing to do if all those things are happening on the same thread OR if you can guarantee that dealloc will run on the same thread as your -postNotification: calls (or doesn’t run like in the case of singletons.)

Where it gets hinky is when -postNotification: can run on any thread (and therefore so can dealloc because -postNotification: will retain/autorelease it before running.)

Here’s the bug:

Thread 1 calls release taking retainCount down to 0 meaning the object will be dealloced. Your object is dead, is just doesn’t know it yet and there’s no coming back.

Thread 2 comes in and posts a notification, for this case let’s assume synchronously since that’s the common case with the observer/selector API. Observer is retained before calling because notification center keeps an unretained reference to observer and wants to make sure the object is alive. Thing is, you can call retain on an object that’s already in dealloc and that doesn’t stop dealloc from finishing and turning that object into a zombie. So now thread 2 is calling into -prompt: and self is in the middle of becoming garbage so nothing you can do in -prompt: will be safe.

Thread 1 gets into dealloc, the observer gets removed and self becomes garbage rendering anything async you did in -prompt: very dangerous. (Doesn’t even have to be async, but that’s the most pronounced case for this problem.)

This same problem manifests with KVO in a nearly identical way and also with notification centers block based API, although in that case it’s actually the internal __NSObserver object that can end up as the bad object being referenced and there’s the added fact that the observer gets retained and then the block gets scheduled asynchronously so if the scheduling hits while in dealloc, the block will almost certainly run after dealloc has finished.

So what do I do?

There are a few options:

Serialization - Run everything on one thread, not exactly elegant, but it does take a lot of the guess work out. This is actually pretty normal if you’re handling notifications in a view controller posted by system API that promises to run on the main thread. Although, this can fall down pretty easily though because making sure dealloc runs on a specific thread when blocks and autorelease pools exist can be incredibly hard.

Block based API - In practice it’s much harder to hit this issue with the block based API due to the lifetime of the observer object it gives back, but it’s definitely not impossible with some well placed autorelease pools and the block based API is also a snake pit of subtle bugs for many developers.

Objects with safe lifetimes - This issue doesn’t present at all if your target is for example a singleton. Not that I’m suggesting you use more singletons, but if you already have a data controller singleton, it’s a perfectly safe target for threaded notifications.

Don’t use notification center - Ya know what’s great? Weak delegates. The self zeroing weak references in ARC are great and one behavior they offer that’s incredibly useful is that they return nil once an object is going to be dealloced, even before dealloc runs.

This code is totally thread safe:

id<MyProtocol> delegate = self.delegate;
if ([delegate respondsToSelector:
  @selector(prompt:)])

{
  [delegate prompt:nil];
}

It’s not strictly required that you use the local ivar, but it’s a good idea since it guarantees that if you get a value back, it will be retained (under ARC) into the local strong variable and live at least through the scope of calling the delegate method.

I’ve always liked delegates. These days I like them even more.

P.S. For people grumbling about how notification center should use weak refs internally, there’s some merit to that, but the unexpected side effects of when weak refs zero can lead to some oddball bugs like if there are multiple observers that are the same except they are registered with different objects (for filtering purposes) and then they all go to zero and you have to decide which one to remove when -removeObserver: is called. It’s not as straightforward as it might seem.

WinterFest 2013

WinterFest 2013 is the “festival of artisanal software for writers.” Tinderbox, Scrivener, Scapple, DEVONthink Pro Office, and Nisus Write Pro are on sale for the holidays.

App Santa

App Santa is “award-winning apps, up to 60% off for Christmas.”

Vesper is $2.99 (regularly $4.99). A bunch of other cool apps are on sale too: Day One, Tweetbot, 1Password, Clear+, PCalc, and more.

Glassboard’s New Home

Second Gear acquires Glassboard. Second Gear is Justin Williams, @justin.

I’m so pleased. I don’t have any business interest in this sale — just the personal satisfaction of knowing that software I helped create is going to a great home.

I use Glassboard every day. It’s the main way Q Branch communicates. I use it with my family, with the local Xcoders group, and with other small projects.

This acquisition marks a personal moment: everything I brought to NewsGator, or created while there, has been passed off to another company. None of my apps remain. They’ve moved to Black Pixel, Push.io, Red Sweater, and now Second Gear.

Q Branch Standard Kit

We just posted Q Branch Standard Kit — QSKit, for short — to GitHub.

It’s a bunch of categories and utilities that we find useful in multiple apps (both Mac and iOS). Nothing earth-shaking or high-tech — just solid stuff. With tests.

Consider it an early Christmas present from us to the community that’s given us so much.

(This is one of the perks of my return to indie life: I can release open source software again. I’m so glad.)

The Seattle Sound

A number of Seattle-ites have apps listed in Apple’s best-of-2013. Gus with Acorn, Chris with Napkin, me with Vesper, Black Pixel with Kaleidoscope, and The Omni Group with OmniFocus and OmniPlan.

A few things are interesting (at least to me) about this list:

  • Only OmniFocus and Vesper were recognized as iOS apps. The rest are on the Mac list. We love Mac software.

  • Only The Omni Group and Gus are purely Washington state. My co-workers are in New York City and Philadelphia; Chris’s co-worker Guy is in Montreal; and Black Pixel, though Seattle-based, has remote workers everywhere. (Probably even in your town.)

  • If you wanted to stretch definitions a bit, you might include Delicious Library, which originated in Seattle and thrived here till Wil ran away to San Francisco. Library, also a Mac app, would not look out-of-place in the list.

  • These are all productivity apps.

It’s possible that there are other Seattle-made apps on the list, and it’s possible that some of those are games. But, if that’s true, then those game-makers aren’t joining the Cocoa community here.

We used to write games here. I’m sure we still do. But as a community we are, apparently, better at productivity apps. Mac apps especially.

(I offer no theories. Not even the easy one that blames it on the rain, because you’d think people make games indoors too.)

Learn How Not to Be Lonely with this One Weird Trick

The promise of Twitter is that it’s live people, but the reality is that it’s Live People Magazine.

Justin Bieber has 47,751,842 followers at this writing. A recent tweet — “Listening to music. #Journals #7days” — has 40,164 retweets and 28,504 favorites. Replies include “@justinbieber love u” and “@justinbieber i love youuuuu.”

Replies also include people begging to be followed back, which is the saddest thing I’ve seen all day. (Admittedly I haven’t been awake that long.)

You might say that’s not fair, but look at the numbers. This is what Twitter is. People Magazine has more substance.

If I go to the website I can see “What’s happening now, tailored for you.” And I see tweets from ESPN (don’t care), Variety (don’t care), Gizmodo (don’t care), and so on.

(I just clicked on a few trending topics, something I never do. I’m reminded why. I can’t bear to describe what I saw.)

Twitter’s job, it’s only job, is promotion. In return it gives people the illusions of information and human connection in 140-character-bites, which is the least — I mean it — the least it could do.

TechHive: Promoted Accounts land in mobile Twitter timelines:

Well, now you’ll see those accounts in your mobile timeline, where you didn’t before. Also, advertisers will be able to add a geotargeted call-to-action message to entice you into following them. This will affect the 76 percent of Twitter users who access the site from their smartphones…

It’s worth noting that Twitter only makes money if users click on Promoted Accounts — the same applies to Sponsored Tweets — so making those ads as engaging as possible is the social network’s M.O.

Every frog likes the warmer water. It feels like that first summer and those carefree tadpole days.

That Enterprise App

Last night at Xcoders people asked me about the enterprise app I mentioned yesterday.

Here’s the story, condensed as much as possible:

In 2005 NetNewsWire was acquired by NewsGator, an RSS reader company with a deployed sync platform. I went along with NetNewsWire to that company. I continued to work on NetNewsWire for years.

Though that company started out as an RSS reader company, it later turned off its RSS reader software as it evolved into an enterprise software company that makes a business-oriented Twitter/Facebook-like-thing (plus extras) that runs behind the firewall. (Called Social Sites.)

I think that’s a pretty a good business. I have zero interest in it personally, but it makes sense, and I believe the company made smart choices, even though that meant it was a very different company from the one I joined.

Though the company had become focussed (quite rightly) on their main enterprise product, I remained working on consumer apps: NetNewsWire, then TapLynx and then Glassboard.

We sold TapLynx and NetNewsWire to other companies. That left me working on Glassboard, in a small spin-off company of six people called Sepia Labs. Sepia Labs was owned by NewsGator, and it had zero employees, but it actually existed as a company.

In all those years I had not been asked to work on the enterprise software. I didn’t want to.

But that changed in late 2012 — mainly because leadership really liked the work we’d done on Glassboard, and they wanted the same team to redo the iOS and Android clients for their enterprise app.

I could have said no, but I would have had to resign right then, and I was unprepared. So I said yes, knowing that I’d have some months to work things out, to either make peace with this somehow or move on.

But that meant actually working on software I didn’t care about or even like. (Not because it’s inherently bad, I should point out — it’s just not the kind of thing I like.)

We worked on it much the same way we had worked on Glassboard: Nick Bradbury did wireframes and I did the Photoshop mockups and assets.

The difference was that I didn’t write any code, where I had written 75% (or so) of Glassboard for iOS. Nick Harris wrote the iOS app and Nick Bradbury wrote the Android version. I was a designer only. (Which is like saying I tried enterprise software but didn’t inhale, I realize.)

NewsGator had treated me extremely well for many years. But a series of rational decisions on everyone’s part (mine and the company’s) meant that I was suddenly working on enterprise software, and I came to realize that I needed a change. There was no making peace with staying.

It was time to come home to indie life, where I’m happiest, where I do my best work.

* * *

I’m never going to be a manager. The fun of this industry, for me, is in making things directly, in getting my hands covered with clay.

And I’m not, at age 45, the best I’m ever going to be. That’s still in the future. But I’m getting closer. My peak isn’t 20 or 30 years from now: ten years is more likely.

As a teenager I read “Seymour: An Introduction” by J.D. Salinger. In that story Buddy, the narrator and a writer, had given his older brother Seymour a new story to read. Seymour liked it well enough as craft, but it fell short of being Buddy-worthy.

Seymour wrote to Buddy, “Keep me up till five only because all your stars are out, and for no other reason.”

I’ve kept that in my head every since. The work that happens when all my stars are out is the work worth doing.

* * *

More from Seymour, just because I like it:

Were most of your stars out? Were you busy writing your heart out? If only you knew how easy it would be for you to say yes to both questions. If only you’d remember before ever you sit down to write that you’ve been a reader long before you were ever a writer. You simply fix that fact in your mind, then sit very still and ask yourself, as a reader, what piece of writing in all the world Buddy Glass would most want to read if he had his heart’s choice. The next step is terrible, but so simple I can hardly believe it as I write it. You just sit down shamelessly and write the thing yourself.

One Year Anniversary

We formed Q Branch on 12/12/12. We wanted an easy-to-remember date.

I didn’t start working on Vesper until the beginning of February 2013 — I waited until I was finished with my then-current job. Though I initially conceived of Q Branch as a side project, it took me almost no time to change my mind and go full-time. (Then I had to give a reasonable amount of notice and wait.) That was one of the best decisions of my career.

A year ago I was a designer for an enterprise app I didn’t care about — or even like in the least tiny bit — and which you’ve never seen or heard of. That’s no way to live.

And now I work with my friends on Vesper. That’s the way to live.

The Stream

These days we all pull-to-refresh for a living.

Alexis Madrigal writes in The Atlantic that 2013 may be the year “the stream” crested:

The great irony is that we got what we wanted from the stream: a way to read and watch outside the editorial control of editors, old Yahoo-style cataloging, and Google bots. But when the order of the media cosmos was annihilated, freedom did not rush into the vacuum, but an emergent order with its own logic. We discovered that the stream introduced its own kinds of compulsions and controls. Faster! More! Faster! More! Faster! More!

I’m not on Facebook, LinkedIn, Instagram, or SnapChat. I’m on Twitter.

Twitter used to be fun, but it’s not fun anymore and it’s never going to be fun again.

Years ago Twitter was the street corner where we all hung out. Craig Hockenberry and Loren Brichter — Twitter app developers — would pull up on their skateboards and show us cool new tricks.

We felt like it wasn’t just a thing-to-do: we felt like we were on to something. There was something new to discover.

It took years for me to learn that the feeling of being on to something is a pretty easy illusion to create. I stopped reading Twitter several months ago and haven’t missed it. I still pay attention to mentions and sometimes I reply — because I don’t want to be a jerk, and I think it’s important for people who make things to be accessible.

The 140-character stream is where things not worth saying, and not worth reading, thrive. It’s where things actually worth saying get over-simplified and then get lost, if they get said at all.

Seattle Xcoders Videos

We’ve started recording videos. Three of them are up on Vimeo: The Invisible HIG by Chris Brummel, UIKit Dynamics by Izzy Fraimow, and Auto Layout: Thinking with Constraints by Kyle Sluder.

There’s a meeting this Thursday. Kyle on Text Kit. I have some experience with Text Kit already, and I’m looking forward to learning more. (After the meeting we’ll go to the Cyclops, as normal.)

Getting Started with Web Services Using Node and Express (for Cocoa developers)

(See Getting Started if you haven’t already.)

Let’s create an API. I could do something with databases — because that’s probably what you really need — but to keep things simple I’ll leave databases out of it. (I might get to them in a future post.)

Don’t Use Node Without It

You need Express. There are a ton of packages for Node — it’s a very active community. Express is a web app framework that takes care of a bunch of common things you’d otherwise have to write yourself. (If Node is Foundation, Express is UIKit or AppKit. Roughly.)

Node’s package manager npm makes installing packages easy. I believe it’s installed with Node, so you should already have it.

New App

Create a folder called expressdemo.

In that folder create a file called package.json. It’s like a Cocoa app’s Info.plist — it contains some basic information about your app, including its name, description, version, and a list of Node packages it depends on.

The file should look like this:

{
  "name": "expressdemo",
  "description": "Checking out Express",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "express": "3.x"
  }
}

If you’re not already in Terminal, open it up and navigate to that folder. Then type:

npm install

This will read the package.json file and install Express and any additional dependencies in a node_modules folder inside the expressdemo folder. (This keeps your app self-contained. You don’t have to wonder if the system has the right modules installed; you don’t have to wonder if the system has the right versions of various modules installed.)

Design the API

Our API does two simple things:

  1. Returns the current date in a JSON dictionary.

  2. Returns the base64-encoded version of a string we pass to it.

We want URLs like this:

/now - returns date
/base64?s=someString - returns base64 of someString

Create the Server

Inside the expressdemo folder create server.js with these lines at the top:

var express = require('express');
var http = require('http');
var app = express();
app.set('port', process.env.PORT || 1337);

Our app requires the express and http modules, and it creates an app by calling express(). It sets the port for listening from an environment variable — but, if that hasn’t been set, it uses 1337.

(Why an environment variable? Because the port shouldn’t be hard-coded. However, it’s useful to have a fallback for when you’re running locally, as we are now.)

Then add the code that starts the server:

http.createServer(app)­.listen(app.get('port'), function() {
  console.log('Express server listening on port ' + app.get('port'));
});

Add the API Code

We have two endpoints to respond to. Both use the GET method. Add the function for /now.

app.get('/now', function(request, response) {
  var d = new Date();
  response.send(200, {date: d});
});

Note the response.send parameters. The first parameter is the HTTP status code, and the second is the response body. Since the response body is a JavaScript dictionary, the response is returned as JSON. (That’s how easy returning JSON is.)

Then let’s add the /base64 function:

app.get('/base64', function(request, response) {
  var stringToEncode = request.query.s;
  var base64EncodedString = new Buffer(stringToEncode, 'utf8').toString('base64');
  response.send(200, base64EncodedString);
});

This time the response body is a string, so it’s returned as itself rather than as JSON.

Note that we got the string to encode via the request.query dictionary.

Save server.js and run it from Terminal: node server.js

Testing

Open a new tab in Terminal and try a few things:

curl -I http://localhost:1337/

The result should be something like this:

HTTP/1.1 404 Not Found
X-Powered-By: Express
Content-Type: text/html
Date: Tue, 10 Dec 2013 05:07:26 GMT
Connection: keep-alive

(The -I switch shows just the response headers.)

curl -I http://localhost:1337/now

The /now endpoint returns the date in a JSON dictionary. The Content-Type header should specify JSON — even though our code didn’t specify that explicitly — and it does:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 40
Date: Tue, 10 Dec 2013 05:09:07 GMT
Connection: keep-alive

The /base64 endpoint does not return JSON, and the Content-Type header should specify text. And it does:

curl -I 'http://localhost:1337/base64?s=foo'

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 4
Date: Tue, 10 Dec 2013 05:10:43 GMT
Connection: keep-alive

The /now endpoint should actually return JSON with something that looks like a date:

curl http://localhost:1337/now

{
  "date": "2013-12-10T05:13:21.772Z"
}

The base64 endpoint should return the input string as base64. We know that foo encodes as Zm9v.

curl 'http://localhost:1337/base64?s=foo'

Zm9v

It works!

Notes

That wasn’t a bunch of code, and you’ve got two endpoints, and one even returns results as JSON. So easy.

Your next step might be to write some unit tests. For that I recommend Mocha.

Getting Started with Node.js (for Cocoa developers)

There are three parts to your iOS or Mac app: design, implementation, and back-end.

Design is what it does, how it works, and what it looks like. Implementation is what you do in Xcode. The back-end is — well, it’s, hrrmmm, something other people do? Or something we just ship without?

What Tim Said

Here’s Omni Group CTO Tim Wood talking about syncing: Own the Wheel. I think this applies to more than just syncing — it applies to whatever back-end services your app needs, even if you’re a solo developer or part of a small shop.

(Unless, of course, you’re writing an app whose reason-for-being is to act as a client for somebody else’s service. Given the experience of Twitter and Google Reader client developers, you might consider this decision carefully.)

There are two pieces of good news. One is that learning this stuff isn’t that hard. The other is that it’s really fun.

Node.js

There are lots of web app frameworks. They don’t all do the same things: you can’t compare apples to apples. Node by itself isn’t even really an app framework — it’s a server framework.

I like it because it’s fast, lightweight, and has wide support. You write code in JavaScript — and, while I don’t love JavaScript, I do like it (to my surprise), and it means one language for server-side and in-browser code.

Node will be instantly familiar to Cocoa developers. It has a main thread run loop. I/O is non-blocking and calls back to your scripts on the main thread. JavaScript has closures (blocks, in our world) which make all this easy.

Install Node

The Node.js downloads page has an installer for Macs. Get it and install it.

Write a Server

Open up a text editor and add the following code.

var http = require('http');

This is kind of like an #import statement. It brings in the http module and assigns it to a variable. (In JavaScript, always declare your variables with var. Always.)

Then create a server. This server does one simple thing — it returns the name of your favorite color.

var counter = 0;
</br /> var server = http.createServer(function(request, response) {
</br />   response.writeHead(200, {'Content-Type': 'text/plain'});</br /></br />   var color = 'blue';</br />   if (counter % 2 === 1) {</br />     color = 'no, green';</br />   }</br /></br />   response.end(color);</br />   counter++;</br /> });

Note the function(request, response)… part — that’s your callback. The server calls it on the main thread when there’s a new request.

Then start the server:

server.listen(1337);

This tells the server you just created to listen on a particular port.

Save the file as server.js.

(Also note that counter variable. There’s nothing special about it — except that there are no locks around it. There’s no need, since counter is always referenced on the main thread.)

Run It

You’ve written a working web service. Now you just need to run it.

In Terminal, from the same folder where you saved server.js, do this:

node server.js

It’s running. (You can quit it with ctrl-C any time later.)

Test It

You can open it in your browser: http://localhost:1337/

Reload the page to see the response change.

Or on the command line (cmd-T to create a new tab in Terminal):

curl http://localhost:1337/

It works!

Notes

Node has a REPL. I use this constantly as I’m learning JavaScript syntax and various APIs. On the command line, just type node and hit return. You’ll get the Node > prompt.

Then you can do things like this:

> var x = {}
undefined
> x.foo = 'bar'
'bar'
> x
{ foo: 'bar' }
> var y = encodeURIComponent('this is a string')
undefined
> y
'this%20is%20a%20string'

The parts after the > are where you type. The result appears below. (Type ctrl-D to quit.)

You can even write shell scripts using Node.

While Node is cool, it’s not much of a web app framework. For that you need Express. I’ll write about Express in a follow-up. (I’ll also write about npm, the Node package manager.)

Though it’s not Node-specific, The Twelve-Factor App is worth reading for a high-level view of how web apps are built these days. (It’s a website/essay. Not that long. Highly recommended.)

You might think you need to buy a server, or rent a virtual server, to run Node — but that’s actually not true. Plenty of places offer Node hosting, where you deploy your app (via Git, usually) to their server, and you don’t have to worry about security patches and all that stuff. See Heroku, Azure, Joyent, Nodejitsu, and others. (I’ve got nothing against buying or renting servers for other reasons. I’m a fan of Macminicolo.net, for instance.)

Also check out Felix’s Style Guide.

Generate HTML from a Folder of Markdown Files

I have some Markdown files that I’m editing and I wanted to generate a folder of HTML files. I looked around on my machine and was surprised I didn’t have anything at hand for that already.

So I wrote a script. I’m a Ruby novice, and it’s quite likely that my Ruby is weird in a dozen different ways. But the script still might be useful to you. (The script requires the RDiscount gem.)

Ack-ack

I don’t own any guns, but the Amazon Air drones tempt me to buy a rifle. It would be so much fun to sit on my front porch and shoot at the drones as they buzz by.

It’s cold today in Ballard but the sky is marvelously blue. Today I’d put on a ski cap and grab a bottle of good whisky and do my part to keep the sky clean.

I might argue it’s a matter of collateral damage. I don’t know how I could tell Amazon’s harmless, happy-day drones from Google’s real-time people-watchers — or those of the police or the NSA. It’s best to shoot them all down.

Or I might argue that it’s just plain fun. So much fun.