Sunday, March 31, 2013

Source "Snapshots"

In our small team of 20 engineers at Coursera, we've been talking a lot lately about what our approach to the world of "open source" should be, both now and long term.

Why open source?

We're all interested in the idea of open-sourcing parts of our code, and we're motivated by a few main reasons:

  • We are heavy users of open-source code in our Coursera codebase, and we would like to give back. Like many startups, we're built on top of a plethora of open-source technologies and libraries - Scala, Play, PHP, Django, Python, Backbone, RequireJS, plus numerous third party libraries built on top of those. One way of giving back is to contribute to the libraries themselves, and we've done that via bug fixes, documentation suggestions, and talks on how we use them. But we would also like to give back by showing them what we've built with their libraries, to serve as examples for other users of the library.

  • We would like to be able to point to our code as a reference in public settings. For example, if we post a bug report about a particular library, we'd like to be able to point to our whole code that uses it, to give the context. Maybe we could elicit a more informed response that way, and someone could suggest a better way to accomplish what we're trying to do. Plus, when I give talks about a particular feature, I'd love to be able to finish by pointing people to the full code for that feature, so that they don't have to guess at the functionality from the snippets I managed to fit on the slides.

Why not open source?

As much as we are motivated to open-source parts of our code, we also have our reservations:

  • We do not have the engineering resources to maintain both a private codebase and public, open-source, community-fed codebase. There have been many interesting discussions lately about the "burden of open source", like this post from Divya after she deleted a popular github repository, and this talk by the Twitter Bootstrap co-creator where he compares the Bootstrap project to a cute puppy that becomes an old, fat, unwanted dog.

    They both realized that when you open source something, you're also creating a community that requires nurturing and a product that requires maintenance and upgrades. Most open source projects do not come close to approaching the popularity of Bootstrap, but even an open source project with a handful of forks and pull requests requires resources and time. For example, my lscache library with only 35 forks has had 5 pull requests over the course of its 2 year existence, and I can distinctly remember putting lscache maintenance on my TODO list, procrastinating it, and feeling bad.

    We're in startup mode at Coursera, and we need to budget our time for internal needs first, without worrying about the potential time it can take to maintain an open-source codebase.

  • We do not always feel comfortable open-sourcing all the parts of a feature. For example, many of our Backbone JS apps communicate with a REST API. We're comfortable with developers seeing our Backbone code, since our JS is already inherently public (in an obfuscated way), but we are hesitant about the server-side REST API side. Maybe it would reveal secrets about upcoming or unknown features, or hey, maybe it has security holes that we haven't yet discovered. That code could also contain secrets like API keys, salts, and hashes, that we would have to carefully remove via a scrubbing script.

  • We do not always have the time to package a feature so that it's "ready-to-run". When you find an open source repository for a particular library or app that you actually want to use, you usually scroll down to find the "Installation instructions" and you hope that it's easy to get it working in a few minutes. As it turns out, it's not that easy to extract bits of a codebase and make them easy for anyone else to get running in their own environment. For example, at Coursera, we have our own custom build tool for our frontend code, we have custom templated configuration files that are used by that tool, and we have several Coursera-specific libraries that we use across all of our frontend apps. Yes, we could either open source all of those things, or figure out how to make our code work without depending on them, but either of those approaches would take significant time and resources.

An Approach: Source Snapshots?

But, as I started off by saying: we really want to give back to open source in some way. We've been mulling over our motivations and our reservations, and I think I've come up with an approach that I can use for many bits of our code, at least in the short term while we're low on resources: "snapshots".

A "snapshot" is a dump of some part of our codebase, taken at a point in time and copied into a public repository. It may be an incomplete dump (missing dependencies or server-side, e.g.), it would not necessarily be runnable, and it would have no guarantees of being up-to-date or ever being updated in the future.

The snapshot would still be useful, for developers looking to see how we approached some aspect in the codebase, and also for us to refer to in talks and blog posts. It would also be a way for us to dip our toes into the open source waters, and to see what developers are most interested in. If a particular snapshot got a lot of attention, then maybe one day, when we felt we had the resources, we would turn it into an actual living open-source library and spend the time needed to nurture that community. A snapshot can serve almost as an MVP, if you think of open source repositories as new products/features.

As an example, I've open sourced a snapshot of the Backbone JS for our forum rewrite, along with a blog post about how it works. If this goes well, we hope to snapshot more of our Backbone apps in the future, as well as the JS UI libraries that we've built on top of Bootstrap and Require.

So that's the hope: we can avoid the burden of open source while satisfying our desire to share our learnings. Let's see how this works. ☺

Rewriting our Forums with Backbone

When we rolled out the redesign of the Coursera class platform back in January, I put up a prominent message asking for feedback, and as can be expected, we got a lot of feedback. Much of the feedback was on the forums, where we had improved the aesthetics but neglected to improve the core usability. We had feedback like:

  • "I want to be able to link to a post."
  • "I can only comments, I can't edit posts."
  • "Whenever I do anything, the whole page reloads and I lose my place."

When I started to tackle the problems, I was faced with our legacy codebase, a spaghetti code of PHP outputting HTML and being manipulated in JavaScript. Some of the actions were done via API-like calls and some were done via form POSTs and server redirects. There was no consistent architecture, and that made it hard for me to make things that felt like they should be minor improvements. There was also an increasing amount of JavaScript, but it wasn't written in a clean way, and it worried me every time I added to it.

So I did the thing that everyone tells you not to do: rewrite the codebase. I knew it would be a lot of work, and that I may risk introducing regressions, but I decided it would be worth it, if it would enable us to iterate faster and innovate more in the future.

The Process

I started by turning the backend into a RESTful API, with logically organized, object-oriented classes representing the routes and the database models. Once I had enough of an API to give me thread data, I started on the frontend, a single-page Backbone web app following the style of our other Backbone apps (which I've spoken about in the past). From there, I just kept iterating, building back the features that we had before and figuring out the best way to approach our top usability bugs.

At a certain point, I realized that I couldn't handle this rewrite myself (a hard thing for me to admit, I may be a bit of a cowboy coder) and I enlisted the help of my colleague Jacob (and his expertise as an avid Reddit moderator and user).

Once we had it 80% done, I started writing tests for the frontend. When we were 95% done, we enabled it via a per-class feature flag for our Community TAs class, and spent a week addressing feedback from the TAs and from our QA team. Then we started enabling it on classes, and after addressing the biggest concern from students (lack of Markdown support in the editor), we've enabled it for all our classes. From start to end, the rewrite took us about 6 weeks - three times as long as I hoped. One day I'll learn that most things take 3x as long as I expect them to. ☺

The Database

Since I wanted to be able to introduce the new forums in old classes - and also because I wanted to scope my rewrite down - I decided to stick with the same database design and model relations.

We use MySQL (in an effectively sharded way because each class has its own database), and this is my not so technical diagram of what our tables look like for forums:

A big thing to note is that each of our threads are always related to a forum, and that we do not have infinite nesting of comments like Disqus or Reddit, we instead have top-level posts which can each have associated comments. We may change this in a future rewrite to allow more levels of nesting with arbitrary comment levels, but for now, the post/comment relation is ingrained into our database design.

The Backend

Our class platform is currently written in PHP, and much of it are custom libraries, but, hey, if you're interested, here's how the new forums backend works:

  • We model the data with the PHP Active Record library, and use class functions and static functions to capture model-specific functionality. We use the Active record functions as much as possible, but sometimes use our own SQL query system (like for INSERT IGNORE, which it doesn't handle).
  • We have a simple Rest_Router class which can recognize registered routes and pass the requests to the appropriate class for processing.
  • We have a routes.php file which lists all of the forum API related routes.
  • We have a file of classes that extend our Rest_Controller class and handle the routes, defining get/patch/post/delete as needed. (We prefer patch instead of put, since partial updates are easy via Backbone and preferable.)

For example, this URL in the routes file is for deleting a user's subscription to a thread:

$router->delete('threads/:thread_id/subscriptions', 'Subscriptions#delete');

This class in the controller file handles that URL:

class Subscriptions extends \Rest_Controller {
  public function delete($params) {
    $response = new \Rest_Http_Response();
    try {
      $request_body = $this->get_request_body();
      $data = json_decode($request_body, true);
      $data['thread_id'] = $params['thread_id'];
      $data['user_id']   = _current_user('id');
      $subscription_data = \Forum\Thread_Subscription::delete_subscription($data);
    } catch (\Exception $e) {
      return $this->error_request($e);
    return $response;

And this is the Active Record model that is called:

class Thread_Subscription extends \ActiveRecord\Model {
  static $table_name = 'thread_subscriptions';
  public static function delete_subscription($data) {
    $subscription = self::get_for_user($data['thread_id'], $data['user_id']);
    return null;

The Frontend

We're a bit of a Backbone shop at Coursera now. We're not absolutely in love with it, but we've built up a lot of internal knowledge and best practices around it, so it makes sense for us to build our new apps in Backbone to keep our approach consistent. However, we do like to experiment in each app with different ways of using Backbone - like using Backbone-stickit for data binding in our most recent app. Sometimes those ways stick and become part of our best practices, and sometimes they fade away into oblivion.

Saying all that, here's a breakdown of how the forum Backbone app works. It's not perfect, but hey, it's a start.

The "Routes"

Most Backbone single-page web apps start with a routes file that maps URLs to views, and Backbone looks at the URL to figure out what view function to kick off. In this case, however, I wanted to code it so I could easily embed a forum thread on any page, regardless of URL. I want widgets, not routes.

To accomplish widget-like functionality, I wrote it so that the main JS file for the forum app looks for DIVs on the page with particular data attributes and replaces them with the relevant view. For example, here's our code for loading in a thread widget:

$('[data-forum-thread]').each(function() {
  var threadId = Number($(this).attr('data-thread-id'));
  var thread = new ThreadModel(id: threadId});
  new ThreadView(_.extend(opt, {
    el: $(this)[0], 
    model: thread,
    mode: threadMode

The Views

All of our views use Jade templates for HTML generation and separate Stylus files for CSS. Many of them listen to "change" or "sync" events on their respective models and then check the changedAttributes() array to see if they care about what attribute changed. That minimizes the amount of re-rendering that has to happen.

As an example, let's walk through ThreadView and its nested views. First, a diagram:

  • ThreadView is responsible for handling infinite loading and scrolling to permalinks. It defers all other rendering and event handling to one of its nested views, which each know to only re-render themselves when relevant properties of the thread change:
    • ThreadHeaderView: manages the title, subscription and thread admin controls.
    • ThreadTagsView: shows tags and handles adding tags.
    • PostContainerView: creates containers for each post using PostView and each comment using CommentView.
      • PostView and CommentView both extend EntryView with no modification. The slight differences between them are handled with if checks inside EntryView (e.g., only a post can be pinned, not a comment).
      • EntryView handles rendering an entry in view mode with its admin controls and voting controls, and it knows how to render an edit mode when the user wants it.

The Models

Most of our models use BackboneRelational, an extension of Backbone that knows how to take a JSON and turn keys into related Collections of Models. Our models also use our custom API wrapper, which takes care of CSRF tokens and displaying AJAX loading messages at the top of the page.

For example, let's look at ThreadModel and its related models. First, a diagram:

  • ThreadModel extends Backbone.RelationalModel, turning its "posts" and "comments" keys into PostCollection and CommentCollection, respectively. It is responsible for fetching thread JSON from the server and for figuring out how to fetch previous/next pages of the JSON. We debated how best to do this, and settled on always passing down a "post skeleton" where each post has an "id" and "order", and then we track which parts of the skeleton we've filled in (based on "post_text" existing), and fill in above/below. ThreadModel also must keep track of which user IDs it's seen on posts, and it fetches user profiles for any new user IDs from our main user database.
    • PostModel and CommentModel both extend EntryModel, and they differ only by their url (as the APIs distinguish between post and comment). PostCollection and CommentCollection are just collections of those models.
    • EntryModel extends Backbone.RelationalModel and is used for saving individual posts and comments - creating new ones and editing existing ones. EntryModel is never used for fetching JSON, because we always fetch on the Thread level, but it theoretically could be if we wanted a standalone entry view one day.

The Tests

We can't reasonably write so much logic in our JavaScript without also writing tests to verify that our logic is sound. We write our tests using the Mocha test runner framework and Chai assertion framework. We use Sinon to mock out our API responses with local test JSON. When we want to test our views, we use JSDOM to render a fake dom and react to fake events, and then we can test that the resulting DOM looks like what we expect. JSDOM does not do everything the browser dom (notably, it's missing content editable support), but it does an awful lot and is much faster than spinning up an actual browser.

For example, here's a snippet of a test for checking that save works as expected:

it('should save new post and render in view mode', function() {

  server.respondWith("POST", getPath('/api/forum/threads/2703/posts'), 
    [200, {"Content-Type":"application/json;charset=utf-8"}, JSON.stringify(postJSON)]);



The Result

You can try out our forums by enrolling in a class and participating, and if you're really curious to learn more about the frontend Backbone code, you can browse our source snapshot here. We accomplished what I set out to do: be able to fix our major issues without feeling like I was hacking the code horribly, and making it easy for me to add new features on top of the codebase, and be able to test them. I imagine we have a forum rewrite v3 in the future, like if we decide to do truly real-time forums or do infinitely nested comments, but I hope that we will be able to re-use much of what we developed for this version. Was the rewrite worth it? I think so, but ask me again next year. ☺

Wednesday, March 13, 2013

Keeping a Product Alive: On-Call & Maintenance

At Coursera, we've split our engineering into three main teams:

  • User-facing: the engineers creating the interfaces that our students and admins use, which often includes an API layer. ⬅ This is the team I work on!
  • Infrastructure: the engineers working on our servers, core service layer, monitoring, logging, testing, and deploy processes.
  • Analytics: the engineers creating tools for data analytics, designing A/B experiments, and writing up their findings for internal learning and research publications.

Product Priorities

As the proud owners of a product with users, we have two main priorities in life (work):

  • Keep the product alive: make sure its up and working as promised.
  • Grow the product: keep innovating and adding the features that we think will bring it to the next level.

Sometimes we think of "keeping the product alive" as the domain of the infrastructure team, the engineers that are on-call and ready when server issues arise. But in fact, we face many little issues that are the realm of the user facing team, issues that arise when users use our product in an unexpected way or in code that's not as well tested. When one of those issues fall on our lap, we have to context switch, decide how important it is, put aside what we're currently working on, and re-adjust our schedule after the unexpected distraction. It's easy to get frustrated as an engineer when you feel like you're spending your time just keeping the product alive and not making progress towards growing the product.

So it's up to us to figure out how we balance those two priorities. What percentage of our week do we spend on bugs vs. features? How do our users communicate what the most important things are to work on right now? How do we make sure engineers don't spend all their time wading through maintenance requests?

After asking ourselves these questions many times for the last few months, we think we've come up with an interesting approach to achieving the balance while making our users and engineers happy.

Product Emergency On-Call

We already used PagerDuty for our infrastructure on-call, and now we've added another on-call line-up to it for our "product emergency on-call". We have a primary ("Captain On-Call") and a secondary ("First-mate Firefighter), and the schedule rotates every Wednesday, right before our weekly sprint planning meetups. We make sure everybody knows who's on-call by plastering their photos on the wall next to the lunch line:

Captain On-Call and First Mate Firefighter

When our Course Ops team finds out about an urgent issue, they send it to a designated email address that both creates a Jira ticket and alerts PagerDuty, setting off our page. Captain On-Call does what they can to resolve the issue, calling on the expertise of the secondary or beyond if it's outside of their knowledge, and often sends out a post-mortem after. We send the post-mortem both so that our colleagues know how to tackle the issue if they encounter it during their duty, and so that everyone knows about any underlying flaws in our platform that might have prevented it from happening entirely.

Product Maintenance Duty

But we don't want to just respond to urgent drop-everything requests, we also want to address the many little (yet important) requests that accumulate. So, Captain On-Call also becomes a product maintenance engineer for the week. We have a Jira filter of issues tagged with product-maintenance-duty, with relevant priorities and deadlines, and Captain On-Call works on those while they're not tackling pages. We know during our sprint planning meeting that they'll be fully dedicated to maintenance that week, so we put nothing else on their plate. Hopefully, by the end of their week on-call, they'll have closed 5-20 of our Jira tickets, learnt about more parts of our codebase than they knew before, and increased the number of tests in our codebase.

We're only on our second cycle of this approach, but so far, it's been pretty successful. It is a bit hard to go from working full-time on a project to working full-time on maintenance, but it's also refreshing. I'd love to hear about your approach wherever you work in the comments.

Friday, March 8, 2013

I'm Bad At Recruiting

As an engineer at a small but rapidly growing startup (Coursera), I know that recruiting is incredibly important, and that referral recruiting is the best source of good employees at this stage.

However, I have realized that I'm just not that comfortable with recruiting, and I think it comes down to two particulars of my personality.

I'm afraid of being wrong

Joining a company is a massive decision - probably the biggest you can make as an adult, outside of family decisions. It's also a very personal decision, based on what you like working on, what your desired work environment is like, and what sort of people you work best with.

So I find it hard to aggressively suggest to anyone that they should join a company, even if it's a company that I'm happily working at. I don't feel like I can know enough about most people to know what company is best for them, and if I do, I worry too much about the implications of my suggestion. What if they join and they don't like it? Do I want to carry that burden with me forever? No, I don't.

As I recall, I've only aggressively recruited one person in my life: Andrew Gerrand, for the role of Go Developer Advocate. I knew Andrew quite well, knew his current job situation, knew his passions, knew the Go team, and felt that it was the perfect match. I also knew that Andrew is an independent, smart individual that would make the decision to join based on evaluating those factors himself, and not just join on my suggestion. He did join, and as we both predicted, he was a great fit for the team, so I feel good about that decision.

I simply do not have the confidence to think that I know what another person should be doing in life (I barely have the confidence to know that about my own life), and that makes it hard for me to impose an opinion on someone.

I'm averse to being a "salesman"

For whatever reason, I am overly wary about becoming an accidental salesman. I'm always on the lookout for signs that I'm selling something without truly believing in it or signs that I'm selling something just because someone told me to. When I was a developer advocate at Google, this wariness was on high alert, particularly since some of us actually had titles like "Sales Engineer".

I like to think of myself as an educator, not a salesman, and that means that when I share information, I want to share *all* the information - not just the positive, but a balanced perspective of what I know. I want whoever is listening to get the full story, and be able to make their decision based on that.

To me, recruiting can feel too much like me trying to "sell" the company - like you're trying to "win" the candidate. I'm not saying that recruiting is always selling, but to someone like me who is overly sensitive to the idea of being a salesman, it often feels too close for comfort.

...But I'm Good at Spreading Awareness

So, I'm not very good at traditional recruiting - like sending emails to people who could be a good fit, manning booths at career fairs, or mingling at networking fairs. But what I can do is spread awareness, as that's what I try to do with everything in life. I can make people aware that we are a company with interesting problems and innovative solutions. How?

  • Tweeting out my day-to-day work life (the good and the bad, of course).
  • Speaking about the technical challenges we've run into and overcome (like on Backbone).
  • Writing blog posts with snippets of code from our codebase and our solutions (like on feature detection for a sign-up process).
  • Open-sourcing parts of our code that the rest of the world could benefit from (I haven't managed to do this yet at Coursera, but it is very high on my TODO list).
  • Answering questions from people interested in Coursera (I often meet for tea to chat with potential candidates that message me).

My hope is that I am doing my part to help grow the company by making more people aware of what we do and how we do it. Perhaps someone will see my posts and think "oh, wow, I'd like to do that" and apply, or maybe they'll see them and think "hmm, nevermind, it's not what I thought". In either situation, we should end up hiring that people that want to do what we do, and not hiring the people that don't, and I think that should be our goal.

I hope. ☺

Thursday, March 7, 2013

Why I Hate Vanity Tweets

I was at a Hertz car rental stop the other day, and as I waited in the customer service line for 20 minutes after spending 10 minutes trying to use their busted self-service machines, I found myself staring at walls and walls covered in ads about how great Hertz was. Apparently, Zagat had rated them best in approximately everything, and they felt the need to spend all of their wall real estate sharing that information with us. I was already frustrated, and their self-aggrandizing house ads just aggravated me more. What benefit was I deriving from knowing how great everybody else thought they were? Why couldn't they use that space to give me some useful information instead?

This reminded me of something else that I see every day that annoys me in the same way: vanity tweets.

What are they?

When I say a "vanity tweet", I mean a tweet or often a retweet that is entirely about how great a particular Twitter user is. Corporate Twitter brands are particularly apt to issue vanity tweets, like this one from @GoogleMapsAPI:

Why do we tweet them?

There are a few main reasons why we make these vanity (re)tweets:

  • We want to thank someone for their compliment, and one way of acknowledging their compliment is to share it with the world. Often times they have less followers than us, so it's a boost to their Twitter reputation when we retweet or mention the complimentary user.
  • We are happy when someone praises us, and we want everyone to know how happy we are - basically, it's a classic humble brag, that thing that we all find so easy to do on Twitter or any social network, because it re-enforces the ego boost that we got from the original action and makes it last a little longer.
  • We want people to look at our stream and think "wow, everybody likes them, so I should like them too!"

Why do I hate them?

As a general rule, vanity tweets provide very little useful information to me as a reader of your stream. Either I already know that you or your product is great and I don't need the reminder, or I don't know it but the vanity tweet does nothing to convince me of it. Or, atleast, I'd argue that it *shouldn't* convince me of it.

What's the alternative?

Here's what I'd suggest instead of vanity tweets:

  • Show, don't tell: Instead of tweeting about how great people think you or your product is, tweet about new ways of using it that I didn't know about, like tutorials or blog posts, or tweet about oft overlooked features. Just hearing that something is great won't convince me that it's great, but if I discover some new way of using it that suddenly makes it great for me, then I might be convinced. Here's an example where Trello points out a customer's unique use of their product:

  • Instead of acknowledging a compliment by retweeting it, favorite it and personally reply to the complimenter with a sincere thank you. They may not get the follower boost that they'd get out of a retweet, but they will be happy for their praise to be acknowledged. You could maybe even ask them what their favorite feature is, so that you can create a useful tweet out of their praise. Here's an example where Trello responds to a compliment and points them to their recommend tool, too:

  • As a general rule for your Twitter stream, be either useful or entertaining to your target audience. If what you are about to tweet is neither of those, then reconsider whether you really want to tweet it. Here's an example of Trello retweeting a useful keyboard shortcut:

Yes, yes...

I don't think that we all need to stop all vanity tweets ever. But I think that we can all safely do less of them, and the Twitter world will be a more useful place. In fact, when I was looking for examples for this post, I was having a harder time finding them than I expected, so perhaps the Twitter world is already a more useful place. Oh, and yes, I'm sure I've been guilty of issuing a few from my own accounts, so this blog post also serves as a reminder to myself.

Sunday, March 3, 2013

5 days in SoCal: Catalina Island, Joshua Tree, and LA

I just took a proper 5-day vacation around SoCal with my best friend and brother, and managed to fit quite a lot into those 5 days- camping, hiking, kayaking, eating, drinking, beaching, the works. And for whatever reason I decided to write up a diary of it, so... enjoy?

Monday, February 25th

  • 12:00 - We all met in LAX - I flew in from SFO, Hunter megabussed from SF, and Stephie flew in from Washington. (Man, I love the view of the cut out landscape from the plane).
  • 12:30 - We took a pre-arranged Karmel shuttle from LAX to the Downtown landing, which took only 30 minutes or so, so we enjoyed clams and sand dabs at the cafe there.
  • 2:00 - We used our pre-reserved tickets for the 2pm ferry to Catalina Island. We set up our 2-person tent for 3, with our Target-acquired 30 degree sleeping bags.
  • 3:30 - We walked to Hermit Gulch and checked in with our reservations. We were the only ones there- it's apparently not quite the season for camping tourism there - maybe because it's actually pretty cold at night still.
  • 6:30 - We tried out the local fare - the grub from the Lobster Trap (the actual food not that great, but their "pancake breakfast" shot was really quite tasty) and the famed "Buffalo Milk" from Luau Larry's (brain freeze alert!). We also charged our phones there since we had no electricity in our lil old tent. Hunter *really* enjoyed his multiple Buffalo Milks - and the bar enjoyed his unicorn mask.

Tuesday, February 27th

  • 7:00 - We awoke bright and early from our not so restful sleep (the ground was hard and cold, and our impromptu pillows sucked), and took a hike up the Hermit Gulch trail. We were sweating by the time we got to the top, 1.7 miles later, and walked/ran the 2 miles down to the Botanical Memorial Garden (pretty but quite small).
  • 11:00 - After a round of omelettes at Joe's Diner, we boarded the "Semi-Submersible", which really truly only submerged about 10 feet under the water. As it turns out, that was deep enough to see a LOT of fish (mostly the same 4 species, but the Garibaldis are cool) and very tall seaweed forests. Also, to giggle at the undersides of ducks and sea lions.
  • 1:00 - Fueling ourselves with chocolate covered bananas, we rented bicycles and attempted the very steep climb up to the "bison spot", or so the map promised. Catalina Island is known for its roaming bison - a film crew brought 10 out there, never brought them back, and they multiplied. Sadly, we didn't really know where to find them on our bikes and probably didn't venture far enough inland, so we biked our tired asses down the hill.
  • 3:00 - We enjoyed a leisurely bike ride down past Pebbly beach and rocks covered in birds, and we were quite happy to spot dolphins just off the coast. So pretty!
  • 4:30 - We took a break at the Avalon Casino, which is quite pretty but was also quite closed (and the museum cost a whole 5 dollars, more than we were willing to pay).
  • 5:30 - As had become our habit, we started our dinner early, this time at El Galleon, this restaurant that had life size replicas of fish all around it (and a waiter who used to be a fishing captain and answered all of our inane questions about them). We stayed there until we managed to charge all of our phones to 100%, and then we went to Coyote Joe's for a nightcap, as we were determined to stay awake past 8. After 5 songs on the Jukebox, a taco, hot chocolate, and a flan, we called it a night.

Wednesday, February 28th

  • 7:00 - We tried as much as we code to sleep in, but we were no match for the cold ground and bright sun. So after packing up and bidding adieu to Killer, the local feral cat, we enjoyed a breakfast at Jack's Diner, a place covered by cowboy quotes and photos of Marilyn Monroe (who spent a fateful year on the island as a 16-year-old)
  • 9:00 - We rented a double kayak from "The Wet Spot" (not what I would have picked for a name, myself, but perhaps I've had too many accidents in my life), and Hunter and I kayaked while Stephie manned our stuff. We kayaked just around the port area for 45 minutes, and in that time we kayaked alongside many many sea lions and even a group of dolphins. Plus, of course, shit tons of cormorants and pelicans. I fell in love with sea lions at that time and decided I needed a pet one.
  • 11:45 - We took the pre-reserved Catalina Island ferry back to Downtown Landing.
  • 1:00 - We took the pre-reserved Karmel shuttle back LAX, where we picked up a car from Hertz rental.
  • 2:00 - We road trip'ed our way to Joshua Tree, passing the land of a thousand wind mills.
  • 6:00 - We checked into the Hi-Desert Motel instead of the campsite at Indian Grove that I'd reserved because we collectively decided that there was no way we would survive a night in the significantly colder desert, given how cold we were on Catalina. We rejoiced in the modern luxuries of the motel - a 20 inch TV, a shower, OUTLETS! We were also happy to find Jesus just next door.
  • 7:00 - We drove down the street to the Joshua Tree Saloon, enticed by the advertisement of Wednesday night karaoke and grass-fed beef. Hunter has never experienced karaoke, and I thought, wow, the chance for another FIRST on this vay-cay. He went page-by-page through the karaoke book, searching for songs that he'd recognize from Just Dance, and when we finally settled on YMCA, we discovered the karaoke machine was busted. Alas, that first was not meant to be.

Thursday, March 1st

  • 5:30 - Hunter decided that it was the most important thing in the world for us to see the sunrise in Joshua Tree, so he woke us up for the "6:20 sunrise". After we all reluctantly woke up, I pulled the shades open and demonstrated that in fact, the sun was already rising quite prettily over our motel and Jesus, and now we were awake for no particularly good reason.
  • 6:30 - We decided to drive into the park anyway and try a morning walk around Barker Dam, walking strategically into allthe sunny bits.
  • 8:00 - Since we made the silly decision of driving into the park before breakfast (do not recommend), we drove back out the other way and found our only option around there was Denny's, where we un-enjoyed some breakfast skillets.
  • 8:30 - We walked around the 29 Palms Oasis Visitors Center, where I proceeded to learn a massive amount about deserts, which fueled my ego for the rest of the trip and will disappear by the next one. We also realized then there were no guided tours we could hit up that day, so we booked tickets for a Keys Ranch tour for Friday.
  • 9:30 - Determined to spend the rest of the day hopping from rock to rock, we headed back in. First we did some meditating at Arch Rock, then interpretative dance at the Cholla Cactus Garden, a quick stop by the Ocotillo patch, aimless wandering at the Cottonwood Spring (i.e. we failed to find the spring), and some learning at the Bajada nature trail.
  • 1:30 - Finally depleted enough to be hungry for our next meal, we drove south of the park to the Chiriaco summit, a truck stop with its own post office, airport, tank museum, trailer park, and school. Most importantly, the cafe had homemade date shakes (vanilla ice cream + dates), which I'm determined to make at home now.
  • 2:30 - dsfsd
  • 5:30 - We timed our entry to Keys View to coincide with sunset and we were well rewarded by the panoramic view of the sun setting over the park, the nearby mountains, Salton Sea, and even a mountain top in Mexico. That, sir, is what we call *high visibility*.
  • 8:00 - We drove to the 29 palms inn, a hotel built around the natural oasis with its own oasis-irrigated garden. We didn't stay in it for priciness reasons, but I figured we could still enjoy its food. We had to sit at the bar for a while at first because we didn't have a reservation, but that was entertaining thanks to the live jazz and more so, the live hitting on of Hunter by the local cougars. Once we got to sit, we enjoyed their homemade sour dough bread alongside our tasty meals.

Friday, March 2nd

  • 8:00 - We somehow managed to sleep in until 7:30 today (a record!), so that meant going to a breakfast place besides Denny's. We went for the Joshua Tree saloon because they have a "breakfast happy hour" and that concept just amuses me. Chili cheese omelettes are delicious, by the way.
  • 10:00 - After some scrambling around the Hidden Valley rocks (yes, "scrambling" is the technical term around those parts for not-quite-climbing-not-quite-walking), we met up with a bunch of people 5 times older than us for the Keys Ranch tour. We learnt how the native americans used the land originally (a diet rich with pine nuts) and how Keys built a ranch, dam, general store, school, and even invented his own back stretcher. Oh, and how he killed a man. Crazy cowboy shoot-outs.
  • 12:00 - We drove back to LA, waving adieu to the windmill gods and singing along to pop rap songs.
  • 3:00 - We got our swimmers on and headed to the beach, where I was immediately distracted by sand and demanded to be buried in it. Stephie and Hunter complied, but when I asked them to make me into a butterfly, they made me rather phallic instead. Sigh.
  • 5:00 - Santa Monica has this amazing "muscle beach" area filled with equipment for the acrobatic at heart, so I swung my heart out while Hunter tried the traveling rings and the not-so-traveling rings. And we mostly just stood in awe as wanna be circus folk walked along trapezes, juggled, hula hooped, and spun their body in crazy ways.
  • 8:00 - We finished off the night by meeting up with our friend Tim, of heavy metal vegan cooking fame, and our last meal was a very British one at the Fox & Hound - scotch egg, bangers and mash, beef and kidney, spotted dick.

Saturday, March 2, 2013

Co-Learning Spaces

I've been toying with an idea in my head lately, and I want to put it out there now to help me think through it and see what you all think.


First, some context. Around San Francisco, we have an increasing number of "co-working spaces", and I've co-worked at a handful of them myself. A "co-working space" is where you can rent a seat, a desk, or a whole room of desks, and you can go there every day and work on whatever it is you do, around other people also doing their work thing. The space provides amenities that you typically get at an office, like Wi-Fi, a fridge and mini kitchen, mail delivery, fax/printer, conference rooms, and often also tries to provide opportunities for networking and business growth. The co-working crowd is vast and varied - book authors, freelancers, contractors, remote workers, fledgling startups, consultants, coaches - and they come across many industries. For them, a co-working space is the alternative to working at home (which can be lonesome and not create the "work/life" barrier that folks strive for) or to renting an entire office space for themself (which can be expensive and requires an investment). Co-working is low-cost and is often a month-to-month commitment, so it's an easy choice for people to make in many different situations.

We also have a few "hacker spaces" and "maker spaces" here in the valley. The classic hacker space is Hacker Dojo down in Mountain View, which is targeted at people "who share a common love of creating wonderful things and understanding how the world works." It's not limited to programmers, but the majority of members likely are. They also host many meetups and (un)conferences along the same lines. A big "maker space" is NoiseBridge up in SF, and they describe themselves as an "infrastructure provider for technical-creative projects." They attract many hardware hackers and host regular classes in both software and hardware.

I think that all of these spaces are fantastic and am thankful that they exist. However, I think we could use another sort of specialized space, particularly around here, and that's what I'd call a "co-learning spaces".

"Co-Learning Spaces"

A "co-learning space" would be oriented entirely around enabling and encouraging learning. All of its amenities, its layout, its target members, its events, its pricing, all of that would be learning oriented. When you walked into the space, you would immediately know you were surrounded by learners and be inspired to learn yourself.


Right, well, that fluffy description is all well and good, but what would that actually look like?

  • The space would include a lab area with inexpensive laptops like ChromeBooks for members to do online learning with.
  • The space would include tutoring rooms, designed to fit a 2-3 people and their laptops.
  • The space would be laid out as an open space with removable walls, to make it easy to turn it into a different layout for different sorts of learning events.
  • The organizers would schedule viewings of online lectures and post them on a calendar, like scheduled viewings of an ongoing Coursera class that multiple members were enrolled in. That would give members an IRL, social component to their online learning experience.
  • The organizers would work with local groups/teachers/skillshare to host low-cost classes for their members and the general public, depending on what their members are the most interested in.
  • There would be a "TIL" session at the end of each week for members to share something interesting they learnt that week.
  • The organizers would collect information about learning opportunities (online and IRL) and broadcast them to their members.
  • The organizers would facilitate the creation of open-source group projects, to give budding software developer members an opportunity to experience both a real project and IRL group collaboration. They might work with local non-profits to come up with these projects.
  • The members wall would list what each member is learning, and what level they're at if they're enrolled in something with known levels like Codecademy and Duolingo. That way, members could find other people learning similar things, and they might form study groups or just friendships.
  • The cost for the space would be a bit less than other spaces, since learners are usually earning less than workers.

As you can see, it would be very similar to the co-working, hacker, and maker spaces that we already have - the main difference is that it would be entirely focused on learning, to its core.


A learning space could easily exist for any sort of learner (of languages, or humanities, of crafts), but at least in the valley, I think we could have learning spaces devoted specifically to learning software development. There just may be enough potential members for that sort of space here, and it would benefit from being a more topically focused space. What sort of people would be members of such a space?

  • Transitioners: People who started off an in industry and have since decided to move into the development industry. We have quite a few GirlDevelopIt members that are transitioning, and the trend is rising. People are realizing that the world needs more developers, and thinking to themselves that hey, they could do that. Some of them do their learning on the side (weekdays/weekends) whilst others leave the current job and become "full-time learners".
  • Recent grads: People who graduated from college, perhaps even with a CS degree, but don't feel equipped enough to enter the job market yet, or have some topics they want to learn more of before they do. CS degrees are great but are sometimes lacking in practical skills, and that's something that many CS grads have to learn in their internships, free time, or summers.
  • Retirees: People who have left the workforce but still enjoy learning. I met a handful of these sorts at the Coursera BBQ - folks that are taking courses just to learn and keep their mind sharp, and they were keen to meet others who loved learning too.
  • Everyone else: We should all be entitled to lifelong learning, and particularly in software dev, we really do need to keep learning since things change all the time. We can't take our whole week off to learn, but we could spend a few days here and there to learn amongst others. I know that I do a lot of extra learning in the evenings and weekends, and I'd love to do that in a space of learners.

Why not bootcamps?

Well, now you might ask, if the members in the space are devoting their time to learning software development, why don't they just join one of the intensive bootcamps around SF? We have quite a few of them now, and they seem to be very effective ways to learn a lot about development in a short period of time.

  • Price: The bootcamps are usually around 6K-10K, and that might be or seem too costly, particularly for someone who is just getting their feet wet. If they are self motivated, they might decide it's better to teach themselves with free/cheap online resources (of which there are plenty) and save their money.
  • Topic: The bootcamps each revolve around particular technologies, like JS, Rails, or Python, and usually aim to produce a particular type of developer (backend/frontend). Someone might be interested in a different technology or want to become a different sort of developer (sys ops, data scientist, mobile, etc).
  • Opportunity: The bootcamps have limited space, and not everybody will necessarily make it in.
  • Commitment: A bootcamp requires 24/7 commitment, which not everyone can do. At a co-learning space, they could be a part-time member and still benefit from their few days a week.

To sum it up...

I love learning, and I love being surrounded by movements that encourage more people to continue learning throughout their life, like GirlDevelopIt with its in-person classes and Coursera with its online classes. I think that something like co-learning spaces could complement the movement towards lifelong education and create a physical space to encourage it. We may not be in college anymore, but we can still be around learners like us.

I'd love to hear what you think in the comments.