Saturday, December 29, 2012

My End of Year Donations

I am not a tax expert (I consider myself accomplished if I do them at all), but I've heard that if you donate to charities by the end of the year, then it helps when you're doing taxes in April. I'm sure all of you know more than me. So, now that my ignorance is out of the way, here's where my end of year donations have gone to:

  • SF Bike Coalition: I am an SF biker myself (not in the hardcore sense of the word, but the commuter sense), and I appreciate everything that the coalition does to make this a more bike-friendly city.
  • Village Harvest: They are a local group that harvests fruit (and lets volunteers help them - I keep meaning to join in, one of these days), and donates them to feed the hungry.
  • Electronic Frontier Foundation: They protect our rights in the digital world, which is an increasingly important issue, with the crazy laws people are trying to pass.
  • Freedom from Religion Foundation: They work to uphold the separation of church and state, something I've been a fan of ever since I was a wee child, refusing to say "under god" during the pledge in school each day. People can have their beliefs, but they shouldn't be enforced on others.
  • Planned Parenthood: They're one of my favorite organizations to donate to, both because I've been a patient of them myself and I like what they provide to others. I wrote more about them in my previous post on donating to reduce birth rate.

So, there are a few days left in 2012. If you're doing any end of the year donations, where and why?

Saturday, December 15, 2012

A Tale of Two Bootstraps: Lessons Learned in Maintainable CSS


The Pull Request

346 files changed, 100 commits, 6 authors.

The title of that pull request? "Port to Bootstrap2." Yes, that was how much effort it took to make a change as seemingly simple as upgrading a CSS framework in our codebase.


The Journey

It all started on a rainy train ride, my daily commute back to San Francisco on the Caltrain. I knew that we used Bootstrap 1 in our legacy codebase (which powers class.coursera.org, where students watch lectures and take quizzes) and we were using Bootstrap 2 in our "modern" codebase (the one behind www.coursera.org, where students find courses to enroll in), and that was making it hard for us to share resources across them, so I thought, hey, I know, I'll just go through and upgrade all our class names in the old codebase. I had upgraded personal projects to Bootstrap 2 in the past, and it hadn't been that hard, especially with the help of the upgrade guide.

As you can guess, it took a wee bit longer than that train ride. Why?

  • Our codebase size: This was only my second foray into this codebase, and so I had very little idea how it worked and how big it actually was. After a few greps for Bootstrap class names, I soon realized that the codebase was about 10x bigger than I eventually realized. I was only familiar with the student side of class.coursera.org, but of course, there's an instructor side, and as it turns out, we have a rather comprehensive admin interface. I also discovered that we had multiple versions of some of our interfaces and we're still supporting both of them, until all the admins have time to learn the new interfaces.
  • Our codebase architecture: Our legacy codebase is written in a hand-spun PHP framework, spitting out HTML with inline JS and linked JS files. I expected to find some CSS class names in the JS, used in selectors or DOM creation, but besides finding a lot of that, I also discovered CSS class names outputted by PHP, and the worst, CSS class names in JS outputted by PHP. Yech! It meant that I had to look in every nook and cranny for class names, from the back to the front.
  • The bootstrap class names: Bootstrap - both 1 and 2 - uses very succinct class names, like "alert", "label", "btn". Those short names are great when you're hand typing out your CSS, but oh wow, they are a *bitch* to search for in a codebase of thousands of files. Do you know how often developers use the word "label"? All the freaking time. Bootstrap 1 even had these short add-on class names for component states like "primary", "success", "info", which thankfully Bootstrap 2 changed into the more specific "btn-primary", "btn-success", "btn-info".
  • The bootstrap JS components: At its core, Bootstrap is a CSS framework, but it offers a small number of JS components on top, and we were using a few of them. After upgrading to the new JS components and making them RequireJS-friendly (so that we could use the same JS libraries in our Require-powered codebase as this legacy one), I had to upgrade the relevant HTML and test that the interactivity still worked as expected. Now, I was no longer making just a visual change, I was possibly affecting interactions instead.

All of those factors combined turned my train-ride of a change into a month-long change, where I roped in more of our engineers, held many "Bootstrapv2athons", and worked with a QA team to do a weeklong test of the branch in multiple browsers.

Finally, we were ready to deploy it. Or, more accurately, my colleague convinced me that we should just get the damn thing out and be ready to deal with anything that we missed. As it turns out, yes, we still did miss a few things- a few big things, a few little things, but we responded as quickly as we could to minimize the damage. Now, finally, we have our entire codebase running off the same Bootstrap version and we can start to share our CSS and JS. Man, was that a journey of epic renaming proportions that I did not imagine.


The Lessons Learned

But I really don't think it should have to be that hard to upgrade a CSS framework. Here's what I want to try next time I work in a codebase that's based on Bootstrap:

  • Do not use the Bootstrap class names directly in our code. They are too short, and it's too hard to keep track of where they're being used.
  • Instead, use a CSS pre-processor like Less (which Bootstrap itself is based on) to extend the Bootstrap class names and define longer, more app-specific, more semantic class names. For example, we might extend btn into "coursera-generic-button" and we might extend btn/btn-success into "coursera-save-button".
  • We would then use those custom CSS class names everywhere instead of the Bootstrap class names, especially if we are using them in our JavaScript.

For a longer article that also recommends that technique for semantic more than maintenance reasons, read Stop Embedding Bootstrap Classes in your HTML.

What do you think? I'd love to hear ideas from those of you who rely on Bootstrap or similar frameworks in your codebase, and what you've done to avoid dependence and ease maintenance. I haven't yet had the chance to test out my strategy, so if there's a better way, I want to hear it.

Oh, on a related note, I hear that Bootstrap 3 is almost out... time to start my next epic branch?

Tuesday, December 4, 2012

Unit Testing our PHP Templates with Selector

I'm finally digging into the codebase that powers class.coursera.org, and it's a wild ride. The original Coursera prototype was built by a few grad students working in the co-founder's machine learning research labs, and like all scrappy prototypes, it was just meant to test whether the whole massively online class idea had any merit to it. As it turns out, it did, and that prototype went on to serve the next class, then the next class, until finally today, it's turned into the code that's serving 32 live courses. Needless to say, those grad students didn't realize when they were first building the codebase that it would one day be handed over to a team of bright and eager engineers who had never seen it before, so its not the most built on the most beautiful architecture or built around an open source framework.

Well, what is it then? It's a PHP codebase, with a custom built router, templating, and SQL querying engine, and it's a fair bit of code. When I first started here, I figured it wouldn't be that much code, based on what I'd seen as a student - but I didn't take into account just how many administrative interfaces power what students see. The professors need to upload their lectures, design quizzes (with variations and pattern-based grading), create peer assessments, view statistics, calculate grades, issue certificates, ban students for cheating, etc, etc. Now that I've dug into it, I realize how much we enable on our platform - and because of that, I realize how important it is to test our platform when we make changes.

Unfortunately, our legacy codebase didn't exactly have a lot of testing when I arrived (I will leave it as an exercise to the reader to figure out how much it did have), so now that I am making changes in it, I'm adding unit tests as I go along, which includes figuring out how to test the different aspects of the codebase, plus how to mock out functions and data.

Testing Templates

My most recent changes have all revolved around fixing broken "messaging" — making sure that students understand deadlines, that they know how many submissions they have left, that they know why they got the score they did — and much of that comes down to figuring out what strings to show to users and what should be rendered in the HTML templates.

Some people may argue that you shouldn't be testing HTML output, because that's simply your presentation, but I would argue that your presentation should be tested, because if a user sees a yellow warning instead of a red one when they've passed the deadline, then that's a bug. Some could also argue that no logic whatsoever should be in your templates, but well, I've never managed to do completely logic-less templates in an app, and I didn't attempt to tackle that goal here.

I started out testing the rendered HTML by testing for string equality or containment , but of course, that was horribly brittle and broke whenever I changed the slightest thing. I soon moved on to using Selector, a library that accepts a string of HTML and lets you query its contents as a DOM, so that you can check for elements and their attributes. It's a better technique because you can check for what matters (like class names) and ignore what doesn't (like whitespace).

As an example, here's the test for our quiz start screen, to make sure that it renders the appropriate data, start button, and an alert message given the passed in parameters.

function test_quiz_start_template() {
    $fake_quiz = $this->fake_quiz(1);

    $rendered = _prepare_template('A:app:quiz:start',
                          array(
                            'quiz' => $fake_quiz,
                            'view_state' => array('time' => 'before_soft_close_time',
                                                  'message' => ''),
                            'retry_delay' => '600',
                            'can_start' => true,
    ));
    $this->verify_quiz_start_template($rendered, 'Untimed', '0/100', '10 minutes 0 seconds', true, false);
$fake_quiz['duration'] = '100';
    $rendered = _prepare_template('A:app:quiz:start',
                          array(
                            'quiz' => $fake_quiz,
                            'view_state' => array('time' => 'before_soft_close_time',
                                                  ),
                            'message' => 'Warning!',
                            'retry_delay' => '0',
                            'can_start' => false,
    ));
    $this->verify_quiz_start_template($rendered, '1 minute 40 seconds', '0/100', '1 second', false, 'Warning!');
}

Notice how that function calls another function to actually do the verifying. Since I'm usually testing the output of a particular template with multiple sets of parameters, I typically make a single verify function that can be used for verifying the desired results, to avoid repeating myself. Here's what that function looks like, and this is the function that actually uses that Selector library:

function verify_quiz_start_template($rendered, $duration, $attempts, $retry, $start_button, $message) {
    $dom = new SelectorDOM($rendered);
    $this->verify_element_text_equals($dom, 'tr:contains(Duration) td', $duration);
    $this->verify_element_text_equals($dom, 'tr:contains(Retry) td', $retry);
    $this->verify_element_text_equals($dom, 'tr:contains(Attempts) td', $attempts);
    if ($start_button) {
        $this->verify_element_exists($dom, 'input.success');
    } else {
        $this->verify_element_doesnt_exist($dom, 'input.success');
    }
    if ($message) {
        $this->verify_element_text_equals($dom, '.course-quiz-start-alert', $message);
    } else {
        $this->verify_element_doesnt_exist($dom, '.course-quiz-start-alert');
    }
}

Okay, well, now you might notice that I'm calling a lot of verify_* functions inside. Those are functions that I've defined in my base TestCase class, so that I can use them anywhere where I want to test DOM output using the Selector library. Here are all the helper functions I've written so far:

function verify_and_find_element($dom, $selector) {
    $selected = $dom->select($selector);
    if (count($selected) == 0) {
      print 'Failed to find ' . $selector;
    }
    $this->assertTrue(count($selected) > 0);
    return $selected;
}

function verify_element_exists($dom, $selector) {
    $this->verify_and_find_element($dom, $selector);
}

function verify_element_doesnt_exist($dom, $selector) {
    $selected = $dom->select($selector);
    $this->assertTrue(count($selected) == 0);
}

function verify_element_text_equals($dom, $selector, $text) {
    $selected = $this->verify_and_find_element($dom, $selector);
    $this->assertEquals(trim($selected[0]['text']), trim($text));
}

function verify_element_attribute_equals($dom, $selector, $attribute, $text) {
    $selected = $this->verify_and_find_element($dom, $selector);
    $this->assertEquals(trim($selected[0]['attributes'][$attribute]), trim($text));
}

Please know that I am not a PHP expert. I used it in college to put together websites and at Google to show developers how to use the Maps API in a few articles, but I never worked on any sizable piece of software written in it. I'm trying to wrap my head about the best practices for PHP codebases, in terms of testing, architecture, and object-oriented design, and this may not be the best way to test template output. I'd love to hear your recommendations in the comments.

Oh, and yes, yes, we don't want this codebase to be PHP-powered forever — but re-writing it will take time and will be much easier once I fully understand it from all these tests I'm writing. ☺

Monday, November 12, 2012

Proving Your Worth as a Self-Taught Developer

In our San Francisco chapter of GirlDevelopIt, we have an increasing number of members that are attending our web development classes because they actually want to get a job in our industry. They come from all sorts of backgrounds, like accounting, publishing, art, and mechanical engineering, but the important thing to note is that many of them do not come from a computer science background. They are learning web development skills and computer science concepts by attending classes like ours, going through online tutorials like Treehouse or Codecademy, taking online courses like Coursera's CS 101, and generally taking the DIY approach to learning.

So, they can do all that to learn the skills that they need for the job, but the question remains, can they actually get a job? A few of them have come up to me and asked for ideas on how they can get their foot in the door in web dev, because they're discouraged by how often they see "Needs X years of experience" or "Must have CS degree" on job descriptions, and wondering if someone like them can actually make it. This is not something that I personally have experience in, because I come from a classic CS background, so I took to Twitter to get ideas from other people.

There were a lot of great ideas, so here's a summary of the suggestions:

Build up your portfolio

To make up for the lack of formal degree and years of experience, build up an impressive open-source portfolio of work.

  • Open-source everything on Github.
  • Make an app for yourself, something that you've always wanted. Ideally, it should have a frontend and at least a small backend (you can do free hosting on Heroku or App Engine for small traffic). Or, maybe it has a mobile frontend, via something like PhoneGap or a native app. The focus of the app should be wherever you are most interested, but ideally it exposes you to all the different aspects of app development.
  • Volunteer to do a website pro bono, like a local museum, art gallery, restaurant, a friends company, non-profit, your kids sports team, etc. Go through the steps of prototyping through finish with them.
  • Make a portfolio site that shows off everything that you can do.
  • But most importantly, open-source everything on Github.

Contribute to the community

Engineering-focused companies like finding engineers that know how important it is to contribute back to the developer community, and there's many ways you can show that.

  • Answer questions on StackOverflow about topics that you know. (Subscribe to the tags you're into).
  • Subscribe to the mailing lists of projects or APIs that you're using, and answer questions that you know the answers to.
  • When you find a bug with an open-source project (even in the docs - especially in the docs), fork it and create a pull request with the fix. The jQuery project has a whopping 5 repos for their docs, and they are very amenable to pull requests from learners.
  • Write blog posts that explain how to use libraries or APIs.

Find a mentor

Many of the local SF bootcamps include mentorship programs, where they pair the students with people already in the industry, and encourage them to connect every few weeks to talk about what it's like in the industry. Mentors also mean connections to jobs and opportunities. If you're not already in a formal mentorship program, then look around for someone local that you admire and ask if they have time to chat with you a bit. It doesn't hurt to ask!

Look for roles that suit your level

  • When applying for a job, look for internships, junior positions, apprenticeships, and freelance opportunities.
  • Consider applying to QA and support positions (both customer support and technical support, like Developer Relations). They often expect less engineering experience, but they can be a springboard to learn more and migrate up in the company or elsewhere.
  • If you see no relevant positions listed at a company you're interested in, write a sweet letter to them asking if they might take on someone like you, who would accept lower pay in exchange for a great experience.
  • Try posting on Craigslist in the jobs wanted section, describing your skills and what you're looking for.

  • Don't expect to get a job on your first try - instead, expect to learn about what it is that people are looking for and about where you need to improve your skills, and then you'll be that much more equipped on your next try. But hey, if you do find a job on the first go, then nice work!

If you're a self-taught developer that successfully found a job in our industry, let me know in the comments how these ideas jive with your own. And if you're a company that's happy to take on self-taught learners, link me to your job page so I can pass it on to our members.

By the way, I'm hoping to learn from these recommendations in figuring out what programs GirlDevelopIt can offer to our members, like a formal mentorship program or a workshop on contributing to open-source. If you're a GDI member, stay tuned!

Monday, October 22, 2012

RemixSouth: My Talk and My Favs

This weekend, I took a trip to Atlanta, GA to speak at RemixSouth, a conference about mobile, web, design, and startups.

My talk was on "When Mobile Web Browsers Attack!". It was all about the bugs that I encountered in the Android and iPhone Webkit browsers during my foray into PhoneGap hybrid web app development last year, and the weird workarounds I found. Most of my audience hadn't done much mobile web development, so it served as a warning for them of what to watch out for, and for those who had, it was more like group therapy. ☺

I also had the chance to attend others' talks. Some highlights were:

  • Responsive Web Workshop". Ben Callahan put on a 3-hour hands-on workshop about responsive web design. After linking the audience to a git repository, he walked through the very basics of responsive design, using CSS media queries, percentage width, and got attendees to try responsify-ing fixed width pages. He also reviewed the tools that make responsive design easier, like Suzy and Semantic Grid, and Media Query bookmarklet, and went over general guidelines for making sites more accessible across multiple devices and environments (e.g. try your website on a phone outdoors - can you actually read it?). He discussed their agency's website development process, like "content priority mocks", CSS-based prototyping, pricing, and testing. He finished with a review of popular responsive design patterns, like for nav bars and sidebar content. It was a great workshop, and I'm hoping that we can get him to give the workshop in SF sometime this year. I'd love to have our designers attend it.
  • "Eliminating Common jQuery Bugs". Elijah Manor gave a talk where he presented buggy jQuery code, challenged the audience to find the bug, and then walked through what was wrong, what the solution was, and ways to fix it. Most of the bugs were ones that I'm already familiar with, or never run into because I avoid things like jQuery animations, but I still learnt a good deal about jQuery's implementation idiosyncrasies. If you'd like to challenge yourself, try out the lab before you review the slides.
  • "CSS3 Animations". Josh Netherton gave an introduced to CSS3 animations. He finished with two Doctor Who related examples, an animated weeping angel and a really cool rotating, spinning Tardis that magically fades in. I still have no immediate plans for CSS3 animations in my everyday life, but it's sweet to see what they can be used for, when that day comes.
  • "Welcome to the Social". Bill Buxton, Principal Researcher at Microsoft Research, gave a thought-provoking talk on apps and devices, and how apps and devices are eventually indistinguishable from one another - i.e. my Kindle is the same app as my Kindle reader app on my iPhone, just with slightly different hardware. He envisions a future where devices are "social" and know more about the other devices around them, so, for example, I might walk up to a digitally projected ad one day, my phone can connect to it, and then I can use the ad as my own canvas. Now, instead of an ad in my app, there's my app in the ad, and the ad<->app relationship is reversed. Really interesting stuff.

It was a fantastic conference, and it was great to meet the web community of Atlanta. Thanks to the organizers for inviting me out!

Tuesday, October 9, 2012

Using Transloadit with Bootstrap

As I discussed in a previous post, one of my first projects at Coursera was implementing social profiles. A big part of our motivation for adding user profiles is to add a sense of community and intimacy to classes, particularly in the place where students interact with each other the most - our forums. The forums were just long streams of text before, and there was one little thing we could add that would break up those streams of text and instantly make them feel like a social experience: user photos!

Upload Strategies

So, in our new profile editor form, the photo upload area is at the very top, and it's the first part of the form that I worked on. Based on my experience from EatDifferent, I knew that photo upload (or file upload generally) wasn't an easy thing to get working across all the different browsers, and I decided immediately that I did not want to rewrite cross-browser file upload from scratch. I started playing around with the popular jQuery upload library, but then I realized that I also had to solve the second part of the upload equation: once I had the photos, where would I store them on the server, how would I resize them, and how would I get them back out? We store many of our assets in Amazon S3, so I started looking at how to use Python to do image manipulation and S3 upload.

Client-side Uploads with Transloadit

Before I got too far down that path, I remembered that there's a new startup that would do all of that for me: Transloadit. They take care of file manipulation (like image resizing and cropping) and file storage (like to S3 and Youtube), and best of all, they provide a jQuery plugin that will do all of that for you, with no server-side scripting on your side. (There's also a very similar startup FilePicker.io, but I happened to meet the co-founder of Transloadit while wandering the streets of Lyon, France, so of course, I was a bit biased in my selection.)

The Transloadit Plugin

Transloadit designed their plugin so that you attach it to a form, and then it listens to the submit, processes the file inputs, optionally throws up a progress indicator, and notifies you with JSON results when it's done. That wasn't quite the experience that I wanted for our profile form, however. I wanted users to select a file to upload and immediately see it uploaded and displayed, while they continue filling out the form. I changed the Transloadit plugin so that it attaches to any DOM node, and processes the file inputs inside whenever a change event is fired for them.

My Bootstrap Plugin

Both because I didn't like the modal nature of the Transloadit progress indicator and because I wanted a more cohesive look & feel to the upload, I came up with my own Bootstrap-based HTML and CSS for the uploader, which includes a progress bar and a hack for actually letting users click a nice pretty button instead of the default (ugly) file input control.

To tie the HTML, CSS, and JS together, I wrote a small Twitter Bootstrap plugin. In that file, I call the Transloadit plugin on the form, specifying options to wait for results, disable the modal, not submit the form after file upload, and also defining various event callbacks. When the user first picks a file, I change the button text and show the progress bar, and as the upload progresses, I increase the progress bar width. Finally, when the file has uploaded, I fade the progress bar and display the image for the user to see. If the user wants to replace the image, they re-click the button and the progress begins again. Behind the scenes, I store the generated S3 URLs (for 3 different resized versions plus the original) in hidden inputs in the form, and that's what goes into our database.

You can see that flow here:



Improvements?

This isn't the perfect photo upload UI, of course. I'd love to give users the ability to crop the photo on the client (like Twitter has now implemented in their avatar upload UI), and I think I could make it more obvious how to change the photo. But, it has served us well enough so far for the profile form, so we're now using the plugin in our admin interface as well, for accepting all the course, university, and instructor media assets.

Fork It!

To make it easier for others to use Transloadit together with Bootstrap like I have, I've put all the code into a git repository along with examples, which show how to use it for both photos and files.

Thursday, September 20, 2012

Creating our Coursera Starter Culture

A month ago, I read "Team Geek" by my former Google co-workers Brian Fitzpatrick and Ben Collins-Sussman. It's a book about how to code well with others, based on their extensive experience both in open-source (they invented SVN!) and their experience starting up the Google Chicago engineering office and leading teams there. I read it at the perfect time, because that was only a few weeks after I joined the half-year-old, 20-something-large Coursera team, just at the time that we were asking ourselves "How should we do X? Should we do Y? What's the best process for Z?"

One of the things that Ben and Fitz talk about is the "starter culture" of a team. For a baker, the most important part of making good bread is to start with a good, strong starter culture, because that culture will overcome any other cultures that come into the bread loaf later and produce a good loaf. They argue that the same can be said for teams — if the founding members of a team start with a strong starter culture, they will pass that on to new members and not be infected by "wild strains" of counter culture. The culture is a lot of things — a shared mission, engineering practices like coding conventions and code review processes, but also social traditions like happy hours or board game nights.

I'm enjoying being part of the first few team members of Coursera, because I have the opportunity to influence the starter culture now, while we're small, and hope that the culture continues as we grow larger. Besides establishing our engineering practices, here are a few company-wide traditions we've started:

Show & Tell

Show & Tell

Remember this from kindergarten? Well, I thought it was awesome, so I've initiated a "Show & Tell" Mondays at Coursera.

We have a whiteboard in the hall where people can sign up with their name and their topic for an upcoming Monday, and then during lunchtime on Mondays, we give 5-15 minutes to each presenter. We're open to anything here — drafts of new features, interesting analytics, tutorials on using internal tools, or even ideas for future features — and it's not just for engineers, it's for everyone. We often get into great discussions after the show & tells about what we've seen, and these days, most everyone goes to them because they don't want to miss out.

It encourages a culture of openness and sharing, particularly across the boundaries of the engineering team. We want to feel like we're all in it together, and that everything we do is relevant to everyone else.



Tech Talks

Once a week, we invite a guest speaker to give a talk to us over lunch, and then we pick their brains to exhaustion after. We're fortunate enough to be working with very smart professors who we've brought in, like Kevin Werbach from the Gamification class that just started, and we're also lucky to work near to smart entrepreneurs, like Michael Staton from Inigral and Gunnar Counselman from Fidelis Education.

It's a great way to expose ourselves to outside opinions and challenge our own opinions, and it's particularly awesome now that we're so small and we can all personally chat with the speakers. We had very impressive guest speakers at Google, but it was rare that I got to actually have a personal conversation with them.



Formal Friday

Well, yeah, we're not the first ones to do this, but we make it look good.

My colleague Leith makes our Formal Fridays more interesting by bringing in a crazy cool raffle item every week, like an elephant-shaped mug or a laser-shooting duck, and sending off an email early in the morning, enticing us with how great our life will be if we were to own that item. The dressier we are, the more entries we get in the raffle, so that email will get us off our asses and into our bowties.

It also turns out to be a great excuse to take team photos once a week, like this one (which was a week I missed, unfortunately):




Games Lunch

As much as we love talking about what we're working on and how we can improve it, it can also be mentally exhausting. We like to give ourselves a break from that every so often with a quick board game, either over lunch or on a late night. We've stocked up on games that take less than an hour to play (or came up with rule variants that make it so), like Dominion, 7 wonders, and Poker. After a rousing game, we feel refreshed and sometimes even inspired. Our product designer Minjeong says she can't help but think about the user experience of playing the game, and how that transfers over to our product.



Those are some of the ways we're starting up our fun, open, and collaborative starter culture. Hopefully we can keep our culture strong as we grow in size. (And yes, we're hiring!) So, what do you do where you work?

Saturday, September 15, 2012

Teaching Web Development in Africa

Note: This is a guest post from Jack Bates, who emailed me a few weeks ago to tell me about something awesome he's been doing with my teaching materials in Africa. I invited him to do a guest post here.

For the past year, I've been tutoring HTML and web development to youth in Africa, and it's been both challenging and fun. Now, our youth village is recruiting someone to contribute more talent to the village next year, and I'm hoping to spread word of this opportunity via this blog post by sharing my experience in the role.

I used the resources that Pamela Fox helpfully compiled at teaching-materials.org to mentor twelve students who all built their own websites, such as websites for their karate club, fashion club, and traditional dance troupe. One student made a website to teach others about the hardware components of computers, and another website discussing the merits of a common currency in the East African Community. The two most advanced students began programming their own computer game to help others practice touch typing, and it allows players to compete across the network with WebSockets.

The youth village and the country are both totally committed to technology. Some of the other things I did this year are coordinate a donation of books from O'Reilly Media, improve internet access with open source tools, and figure out wireless and AC electrical problems. Plus, I installed the first solar heated plumbing in the village, on the roof of my staff house. During the school vacations, I got to travel to Uganda, Kenya, and Tanzania. The village is in rural Africa, and there's a passion fruit vine outside my window.

I work with four permanent Rwandan computer staff who are better teachers than I'll ever be. Some of the kids are afraid of computers when they first get to the village, but I've watched these teachers encourage them, and now a girl who was afraid before is one of the leading computer students. Sadly there are still fewer girls than boys who are passionate about computers.

This position is also part of a cohort of about ten international volunteers, with different roles and responsibilities. If this year is an example, they are highly talented and interesting people.

I was enthusiastic about doing this because it took me a long time to learn to code, and looking back, many lessons could have been way easier. I think tutoring others is the best way to recover that investment. Admittedly, the hours are long (sometimes I think Rwanda is the Japan of Africa, with its insane work ethic and dense population), and there are plenty of frustrations and contradictions, but overall, it is a sincere, kind, and optimistic country.

It would be awesome to have someone who is really excellent with web technology in the village next year! If interested, check out the position description and email volunteer@asyv.org with any questions.

This site is not affiliated with or sponsored by the Agahozo-Shalom Youth Village nor has Agahozo-Shalom approved, reviewed or confirmed any of the data and information provided herein.

Sunday, August 26, 2012

Donating to Reduce the Birth Rate

Now that I'm actually earning income again, I've decided to start donating much more to charities - both because it will reduce my tax burden, and because I feel like my money would be better spent elsewhere. I have no kids to support and no mortgages to pay and have no plans for such things in the near future, so I only really need enough money for SF housing (which is actually a fair bit), food, and travel.

So, I've started researching charities and causes. Recently, partially due to the influence of Isaac Asimov's essays, I've become very interested in the problem of lowering the global birth rate. We don't have enough resources to support the growing population of the world, and while of course we should work on ways to increase our resources, we should also think about decreasing the population growth rate. As I see it, there are a few ways of doing that:

  • Increasing the availability and contraception and family planning. For the women who don't want children, we need to make it easier for them to prevent it from happening.
  • Increasing the number of women in the workplace. For a long time in human history, the role of an adult woman was to bear children and feed them, and we haven't had time for other occupations. We needed that division to make sure that enough children would survive to continue on the human race. But now, we don't have that problem, and adult women should feel empowered to spend their time elsewhere. If they have an education and/or jobs skills training, then they should hopefully find a job opportunity awaits them. I'm not saying that no women should be mothers ever again - just that there may be some women that would happily spend more of their time in the workplace than in motherhood if they were given the chance - and we should make it possible for them to get that chance.

Given those thoughts, here are the charities I donated to:

  • Planned Parenthood San Francisco: They provide fantastic services and support, and I'm proud to say I'm both their patient and now a sponsor.
  • Bedsider: They are an online birth control support network, and they're going about it with a modern website and attitude.
  • Marie Stopes International: They're an international organization dedicated to family planning and reproductive healthcare. According to them, they prevented 4.5 million unwanted pregnancies in 2011.
  • Girls Inc: They're a US organization dedicated to helping girls to achieve and aspire.

The hard part about donating to charities is not knowing which of them will do the most with your dollar. There are sites like GiveWell and CharityNavigator, but they haven't reviewed all the charities out there. So, I'm distributing my donations in the hopes that I'll achieve my goal through at least one of them. Let me know if you have any suggestions for ways to maximize the impact of my donations in this area. Thanks!

Thursday, August 23, 2012

Using the Maps API Autocomplete for User Profile Location

As a frontend engineer at Coursera, one of my current projects is to create shared profiles for our users. When users first sign up, all we ask them for is their name, and so that's all we can show to other users as well — like when they see each other's posts in the forums. We want to make it so that users can click on a classmate's name and find out more about them, like where they're from and what other classes they're taking, because that should make a classroom of thousands of strangers feel more intimate. Eventually, we also want to make it easy for users to search their classmates to find people to form in-person, location-specific study groups or language-specific discussion forums, because that will give users a mini community inside the class.

By the time users sign up for Coursera, they've already signed up for many other sites and likely filled out a profile information many times, and I wanted to make their Coursera profile editing experience as quick and easy as possible. Easier said than done! It's amazing how many debates and decisions can go into designing a single form.


Location, Location

For example: location. We want to let users share where they are—not their exact address, that'd be creepy—just their city and country. In my first draft of profiles, I started off with a single line text input for location, with a placeholder for "City, State, Country". But, I wanted to be able to query our database and easily find out which users are from what country or state, so I needed a good way of turning user entered locations into structured data.

Halfway down the path of implementing it myself, I discovered that the Google Maps API now offers an Autocomplete API and Autocomplete widget, and that it was exactly what I needed. When you attach the widget to a text input, it listens to a user typing and pops up suggestions (like Google instant search), lets them pick a suggestion, and it can call back your code with structured information about the picked address.

The Code

To use it for our profile editor form, I first created HTML for the single-line text input and a set of hidden inputs for each piece of structured location data that I cared about. We actually write our HTML in bracket-less Jade templates, but here's what it comes out as:

<input name="location" type="text" placeholder="City, State, Country">
<input name="location_city" type="hidden">
<input name="location_state" type="hidden">
<input name="location_country" type="hidden">
<input name="location_lat" type="hidden">
<input name="location_lng" type="hidden">
<input name="location_id" type="hidden">

Then in the JavaScript, I attach the Autocomplete widget to the location input and add an event listener for every time the user selects a new result. In the callback, I populate the hidden inputs with the relevant structured data. The results format for the Maps API is a bit complex, because they designed it to work for any address anywhere in the world (and not every address is divided into the same city/state divisions as US addresses), so you end up having to search through all the "address components" to find the component that most matches what you're interested in. To simplify that search, I wrote a wrapper function using Underscore's find method. Here's what that looks like:

var autocomplete = new google.maps.places.Autocomplete($('#input[name="location"')[0], {types: ['geocode']});
  
google.maps.event.addListener(autocomplete, 'place_changed', function() {
    var place = autocomplete.getPlace();
    $('input[name="location_id"]').val(place.id);
    $('input[name="location_lat"]').val(place.geometry.location.lat());
    $('input[name="location_lng"]').val(place.geometry.location.lng());
    $('input[name="location_country"]').val(findComponent(place, 'country'));
    $('input[name="location_state"]').val(findComponent(place, 'administrative_area_level_1'));
    $('input[name="location_city"]').val(findComponent(place, 'administrative_area_level_3') || findComponent(place, 'locality'));
});

function findComponent(result, type) {
  var component = _.find(result.address_components, function(component) {
    return _.include(component.types, type);
  });
  return component && component.short_name;
}

All the code is in this gist.


The Result

We launched the first iteration of profiles today, so you can try it our yourself, in your settings. Or you can check out this screenshot of what it looks like to a user when they're editing their profile.

When a classmate is viewing their profile (like you can view mine here, they'll see their location plus a little flag (courtesy of this free flag icons database), and that flag is based on the hidden country field.


Caveats

There are a few things to keep in mind with this approach:

  • If the user types their location but does not select it from the autocomplete, then we will not store the structured data. I could add in a pre-submit reverse geocode step in case that happens, but I'll wait until I see what users do.
  • If the user types a location that the Google Maps API simply doesn't know, then we won't have any structured data about it. That's okay- we'll still display what they typed on their profile, we just won't be able to do any clever location-based recommendations for them.
  • The Autocomplete widget includes the Google logo, and per the terms, that cannot be removed. I am okay with admitting that it is courtesy of Google, though. I think it improves the user experience enough that it's okay to share branding with them.
  • If the Maps API ever shuts down the Autocomplete widget (Google is on a bit of an API-killing roll these days), we will have to re-write the functionality ourselves. That's okay, we'll still have the data from before. There's no harm in saving time now while we can.
  • Per the Maps API terms of use, you shouldn't be storing the Places data forever - so you should store the place ID and refresh your cache every so often. Or at least, that's how I interpret the terms. Do your own interpreting, or hire a lawyer. :-)

All in all, it's a nice touch in our profile experience that should hopefully make it faster and more fun. Thoughts?.

Tuesday, August 21, 2012

Geo-Targeted House Ads

At Coursera — like many companies in the valley these days — we're desperate to hire more people, particularly for our engineering department. But hiring is hard, and the reasons can be boiled down to these: 1) there aren't enough qualified candidates in the world, 2) the qualified candidates that do exist don't know that we exist, and 3) the qualified candidates don't want to work for us. Problem #1 isn't something we can quickly solve — though hopefully we are indirectly helping to solve it by offering so many CS courses for free. Problem #3 is also not easily solved — candidates may not like our technology stack, our location, or our culture — all of which are hard to change. So, in the short-term, we're focusing on Problem #2, making sure that candidates know we exist.

Well, I realized that we might actually have potential candidates amongst our Coursera users, and I wondered if there'd be an easy way to let them know that we're hiring. I didn't want to message all 1 million+ of our users, since it wouldn't be relevant to many of them - like those in countries with visas we don't sponsor, or those studying courses outside of CS. So, I decided to display a "We're hiring!" house ad based on the combination of geolocation and course enrollment information. It's my ghetto version of a geo-targeted house ad, and I thought I'd share the idea and code here.

Here's what it looks like — quite basic:

Here's the HTML for the jobs ad, which uses the Twitter Bootstrap alert classes:

<div id="coursera-promo-cs-job" class="alert alert-info coursera-promo">
  <button data-dismiss="alert" class="close">x</button>
  Want to help us build the best online education platform?   <a href="/about/jobs">Join our team!</a>
</div>

And here's the JavaScript. I use the jQuery cookie plugin to check if the user already dismissed the promo, then use our internal methods to check if the user is enrolled in CS classes, and then finally, if I've made it that far, I load the Google AJAX APIs and use the google.loader.ClientLocation object to figure out if the user is in a target country. If the user meets all the conditions, then I show the ad and add a click listener to the close button. I also use our internal analytics to track when users are shown the ad and when they click or dismiss it.

var promoId = 'coursera-promo-cs-job';
if (!cookie.get(promoId)) {
  if (Coursera.user.isInCSClass()) {
    $.getJSON('https://www.google.com/jsapi?callback=?', function () { 
      if (google.loader.ClientLocation) {
        var countryCode = google.loader.ClientLocation.address.country_code;
        var state = google.loader.ClientLocation.address.region;
        if (countryCode == 'US' || countryCode == 'AU') {

          var $promo = self.$('#' + promoId);
          $promo.show();
          Coursera.tracker.push({key: 'user.promo.show.' + promoId});

          $promo.find('.close').on('click', function() {
            $promo.hide();
            cookie.set(promoId, 'closed');
            Coursera.tracker.push({key: 'user.promo.close.' + promoId});
          });

          $promo.find('a').on('click', function() {
            Coursera.tracker.push({key: 'user.promo.click.' + promoId});
          });
          
        }
      }
    });
  }
}

When we first deployed this, I set the targeting quite narrow, restricting it to only users in 2 CS courses and from California. Since then, we've broadened the targeting to all of US and 5 more countries.

As we find the need for more "house ads", like promoting new features that may be more relevant to some users than others, I imagine I will turn this bit of code into a more general purpose library, and maybe we'd move the logic into a database or deploy script, to make it easier to configure ads without changing code. Do you do anything similar on your own sites? Let me know in the comments.

Monday, June 18, 2012

Porting to the Paid Google Translate API v2

Last year, one of the little apps that I put together was Translation Telephone, which lets you enter a phrase and translates it across 20 languages until finally translating back to the original language. It's interesting to see how some phrases transform - and sometimes the results sound almost like the output of an intelligent being. For example, "Capitalism: God's way of determining who is smart and who is poor." transformed into "Capitalism: God's way of determining who is smart and who is poor."

Unfortunately, not long after I made the app, Google announced it was shutting down the Google Translate API. There was a pretty big outcry after this announcement, and Google eventually decided to replace the free API with a paid translate API. Well, it's taken a while, but I've finally ported my app over to the paid translate API. I thought about replacing it with a non-Google free translate API, but I didn't find many good options, and the Google Translate API pricing is transaction-based and inexpensive: $20 per million characters of text.

To port over to the paid Google translate API, I did a few things:

You can see all the code changes in this commit. Once I made the v1-compatible wrapper friendly, I didn't have to change much of my original code. And maybe it will help other folks to port more easily.

Lesson learned: I've been putting off this port for half a year now, and it only actually took 20 minutes. I should have just spent an afternoon trying to port earlier instead of telling myself it would take a long time. One never knows with ports!

My Next Step: Coursera and The World of Online Education

Over a year ago, I left Google to go out in the world and try and figure out what I wanted to do with my life. I spent much of the last year "hermit hacking", mostly on EatDifferent, and as I explained more in my recent Ignite talk, I realized something very important during that time: as much as I enjoy working on projects that I love and believe in, I can't do it alone. I need to be around other people working on the same project, who believe in the same mission, with energy and passion that I can feed off of. When it is just me on a project, I get lonely and lose confidence in what I'm doing. So I decided that it was time to join a team. But who and what?

I've always been plagued by the fact that I'm interested in too many things - language, travel, science, evolution, play, food - because my multitude of interests makes it hard for me to pin down what I want to focus the majority of my time on. But I think now I've figured out the common thread amongst everything I like working on: education. I love learning and teaching, across every topic and in every form. The more I think about it, the more I realize how much of my projects have actually been about education. In college, I spent my extracurricular time organizing workshops. At Google, I used my role in developer relations to figure out how best to teach APIs to developers (and have them teach eachother). In my spare time, I've taught at computer camps, created curriculum, tutored students, and made apps that enable learning like WordHunter and QuizCards. Even my mild obsession with nutrition for the last year has revolved around learning and teaching.

So, I figured out the "what": education. But who? There are a lot of interesting things happening in the education space right now — there are people putting together intensive academies to teach programming, there are efforts like Khan Academy to improve K-12 education, there are startups like Codecademy to teach programming to the masses, and of course, there's developer relations across API and web focused companies like Adobe.

I was in the midst of checking out all those possibilities when I attended FooCamp last weekend, an unconference bringing together people across many fields. I naturally found myself attending many of the education-focused sessions and ended up chatting with Andrew Ng, the co-founder of Coursera.

Coursera is the startup behind the online university level education (first from Stanford, but now from a few other top-notch universities), and their mission is to make quality education available for free to everyone. They've had a great start, with now 1 million sign-ups for their classes, but that's the thing: they're really just getting started. There's a lot of experimentation to be done to figure out how to best deliver online education: how to get students to stick with it, how to get a community growing around it, how to do peer grading, how to make their credentials matter.

After my chat with Andrew, I realized that there might be ways that I can contribute my experience, skills and ideas to their mission, and well, to make a short story shorter, I signed the job offer a few days later. On July 2nd, I'll be starting as a Frontend Engineer at Coursera, and I'm super stoked. An awesome team and an awesome mission, what more can a girl want?

Oh, and hey, now I get to put in an obligatory plug for my new company: Coursera has tons of courses and several job openings. Join us! ☺

Friday, June 15, 2012

A Year of Hermit Hacking

A bit over a year ago, I announced that I was leaving Google and going out in the world to see what it held for me. I spent the time since then as a "hermit hacker", working on my own projects from my home, cafes, co-working spaces, and really anywhere I could get an outlet. I learnt alot about myself and what I need in life to be happy, both physically and mentally.

I decided to share my story in an Ignite talk at Foo Camp last weekend. You can check out the slides below (or on SlideShare) or you can watch the video on YouTube.

...so that was my personal experience with hermit hacking. What's yours?

Tuesday, June 12, 2012

Alternatives for After-Parties at Conferences

This post is based on a discussion and brainstorming from FooCamp 2012.

I attend a lot of conferences, either as a speaker or attendee, and back when I was at Google, I even organized a few conferences. I love conferences because they give me a chance to learn a huge number of new topics at once and to meet developers like me - but I don't love everything about them. I'm increasingly disappointed with the decision by most conferences to host their after-parties at bars, and for the only activity to be drinking and networking. Before I go into why, first let's answer this question...

Why are most after-parties at bars?

Conference organizers want their attendees to meet eachother, and an open space like a bar provides the potential for every one to meet eachother. Most attendees are strangers, however, so they add in the alcohol to loosen people up so they have the balls to approach eachother - aka, "social lubrication."

Well, what's wrong with that?

Well, first of all, there's an over-reliance on alcohol to provide the social lubrication. There are many people that don't drink - pregnant women, recovering alcoholics, or just people like me that don't think it's the healthiest of activities. So, for those people that don't drink, they're left with no means of social lubrication but they're still in the awfully intimidating situation of being in a sea of strangers. When I enter a bar and don't immediately recognize anyone, I hide in the bathroom and come out every so often to see if I suddenly see someone I know... or eventually give up and leave. Yes, I'm probably more shy than others, but I'm sure that others feel some degree of intimidation in these situations.

Secondly, there's a danger on relying on alcohol as a social lubricant, because as I think many of us know from college parties, alcohol doesn't just make it easier to talk to eachother - it also makes it easier for us to do things that we shouldn't, and to cross physical boundaries that we often don't want crossed, particularly at a conference. When I'm a conference after-party and there's an open bar, I make doubly sure that I stay sober and aware, so that I can try and figure out if any of the attendees have shot past the limit of knowing the boundaries.

Alcohol besides, there are also logistical problems with bars: there's often loud music that makes it hard to hear eachother, the space is usually structured in a way that makes it hard to move freely (so you can get stuck in one conversation forever).

So, what are the alternatives?

Well, we should address a few needs in suggestions alternatives to the bar: 1) we want a way for people to get to know eachother, and 2) we want to provide a space for people who don't feel comfortable around alcohol or people drinking.


The Un-Bar

One basic alternative to the bar is the "un-bar" - an alcohol-free bar that would serve tea, juices, and the like, and would be an option for folks who don't want to be in the same space as drinking. However, the "un-bar" doesn't provide a solution to the social lubrication question (besides giving people the obvious conversation starter of "so, why are you in this bar?"), so there's more work to do.


Conversation Starters

Most people want to meet new people, but don't know the best way to start a conversation with a particular person. Sure, you can go up to someone and say "so, how do you like the conference?" but it always gives me more confidence if I have something more personal to say them, some reason why I'm talking to them and not someone else. Here are a few ideas:

  • People Bingo: Create bingo-like cards where every square is an attribute of a person like "Lived in Europe" or "Has 3 kids" or "Loves Python" (the attributes can be tailored to the conference or crowd). Then, give every attendee a different card, and tell them that they'll get some piece of inexpensive schwag at the end of the night if they get bingo. Attendees then can go up to eachother and say "Sooo...ever lived in Europe?" and that can launch them instantly into conversation. We did this once at a networking event in Australia, and it was a lot of fun. We also did something similar to people bingo at Webstock a few years ago, led by reality games expert Jane McGonigal. It was a people-oriented scavenger hunt where she gave us a mission ("find someone with a purple accessory!") and we had to find that person, bring them back, and have them introduce themselves. Lots of fun.
  • Conversation Cards: A less game-y version of People Bingo would be to simply provide "conversation cards" for attendees to pick up at the door. To avoid them coming across as super-lame, the questions could be fun what-if style questions - or stick with lame and watch attendees bond over the lame-ness of it.
  • Badged Name Badges: Most conference attendees wear name badges, so it's a great place to stick conversation starters. Conferences can encourage attendees to write tags on their badges for their main interests or give them a variety of topical stickers to decorate their badges. The NotConf in Texas let us pick from stickers like Android/iOS/JS/Python/beer/wine/etc, giving us some basic prompts (ie. "You like Android?? Psh!"). If you do this, try to make the badges and decorations as visible as possible, so that people can approach with topics from afar.
  • Tribe Tables: This is more for conference mealtimes than for after-parties, but you can do it wherever you have people sitting around tables. Figure out topics that interest your conference attendees (like based on the tracks or talks) and then put a big sign at each table declaring its topic. People will sit at the table who are interested in what's on the sign, and they'll have a more specific thing to talk about with eachother. The tricky part of this is getting the distribution of topics right so that most people end up at tables matching their interests, but in case it fails, they can always talk about how they're *not* into that. Many conferences already do this, like OSCON and Google I/O.
  • Ice Breakers: When there is a small enough number of people, you can actually kickstart an event with a round-the-room ice breaker. At FooCamp, we managed to do an ice breaker with 300 people, and we each said 3 things we're interested in (no verbs, just nouns), and then our name. That way, you could keep your ears perked for topics you were interested in, and remember the name of whoever said it. You can also do the kind of ice breakers we did as kids, like saying your name and a fruit that starts with the first letter. Once again, attendees could bond over the lame-ness. :)

Bonding Activities

Sometimes after a long day at a conference, I'm tired of conversing but I still want to spend time with the other attendees. That's why I love when conferences provide actual activities in addition to a networking space - like games, crafts, or recreation. Here are some ideas, but you can really just think of any activities you loved doing in recess, summer camp, afterschool, etc. You know, when you were a kid and couldn't resort to alcohol for fun.

Fun & Games

jenga
  • Trivia Night: This takes a bit of effort, but can be well worth it. Create trivia questions that are topical to the conference, organize people into random teams, and have them compete to win some pile of schwag. To make it so more teams can win stuff, offer bonus questions throughout the night.
  • Poker/Casino Games: These can be fun, as long as you don't spend the whole night losing (like I often do...) Don't involve real money, though - just start everyone off with a certain amount and make it tradeable for schwag at the end.
  • Dominos/Jenga: At JSConf, they setup a game of Jenga inside the crowded bar. Not only did you have to be careful when extracting pieces, you had to worry about the random people walking by that had no idea there was Jenga behind them. :)
  • Board Games: You can do strategic games like Settlers of Cattan, but they can take quite a while. I prefer more of the "party games" like Pictionary, Charades, and Balderdash.
  • Parlour Games: My favorite of these is WereWolf/Mafia. I've spent many nights of my life playing WereWolf until 3am at conferences. You've never bonded with someone until you've looked them in the eye and accused them of being a blood-thirsty werewolf. Another fun one we played at FooCamp was Psychiatrist. This game only really works if you find a person that's never played it before, but if you do find that, it can be pretty hilarious.
  • The Sentence Game: A cross between pictionary and telephone, and only requires pencil, paper, and imagination.

Arts & Crafts

  • Drawing: Instead of tablecloths on your tables, do what many restaurants do and cover them in butcher paper with crayons nearby. People can doodle as they chat, and chat about their doodles. You can refresh the butcher paper throughout the night when it gets covered, and stick the creations on the walls for passerbys to chat about.
  • Crafts: At the Japanese-themed Heroku Waza conference, they had tables in the open area where you could do origami or linoleum cutting along with pros. It was a fun way to spend 30 minutes and bond over what we were making.. it was actually so fun that I didn't attend many talks. So, don't offer it at the same time as talks if you want to maximize audience size. Other fun crafts: boondoggle, friendship bracelets, sand art, finger painting, etc.
  • Maker bots: At FooCamp, there was an area with maker bots where you could actually get your face scanned in 3d and wait for a print-out of the face. People love seeing the technology and talking about how it works - the only problem is that it can't handle a lot of people at once. Definitely a fun side booth for a technical conference, though.

Getting Physical

After a day of sitting in rooms, sometimes I'm itching to move around. Just keep in mind the risk inherent in each activity and your liability if someone gets hurt, and for activities where people's bodies are mingling, remember that not everyone will be comfortable with that and it is more likely for boundaries to be crossed.. those activities may not be appropriate for all conference crowds. Also, sadly, not all conference venues have the space necessary to do physical activities.

  • Twister: At a retreat I ran in college, we used to set up giant games of Twister using paper plates colored with markers, so quite a few people could play at once. Ridiculous but fun.
  • Adult Hippity-Hop: Bring back the simple childhood joy of hopping around on a giant ball.
  • Floaty Ball: At FooCamp, there was this huge inflated ball-thing with LEDs that we played keep-away with in the lawn. It was stupidly simple fun and quite beautiful against the night sky.
  • Frisbee: Somehow, throwing a disc around is pretty amusing, and it's an easy game for people to jump in on.
  • Mechanical Bull Riding: One of the JSConf after-parties in Arizona was at a bar with a bull, and everyone had a go at trying to stay on. It was quite entertaining, but it did get a bit awkward at times seeing people's bodies flail wildly (and the bull DJs at the bar kept jiggling the girls at opportune times... would have been nice if they refrained from that.)

What else?

This is not a comprehensive list, but it should hopefully get you thinking about alternative ways for encouraging social interaction at conference events. It's not that I expect every conference to remove drink-ups from their schedules, but I do hope that organizers spend a bit of time thinking whether they can mix it up a bit. And, hey, doesn't a lot of this sound like a lot of fun?

Let me know in the comments about your experiences with conference after-parties, and if there's more I should add to the list.

Tuesday, June 5, 2012

Client-Side Storage Article Series

After giving a talk a few months ago on Client-side Storage, I was asked my DrDobbs.com to write it up in article form. Well, thankfully, I'd actually written most of the slides up already in my speaker notes, so it wasn't too much work to article-ize them. We split the article into two parts: 1) Understanding Client-side Storage in Web Apps, an overview of all the options like cookies, file system, and indexed DB and then 2) the localStorage API, a deep dive into localStorage and ways to use it. Give them a read - they might give you an idea for ways to use client-side storage that you hadn't thought of before.

Now, if only I had time to turn all my talks into articles...!

Monday, May 28, 2012

Using Grunt.js with CSS

For most of my apps, I've been using either a Makefile, shell scripts, Python scripts, or some combination of those as my build tool — taking care of tasks like concatenating files, minifying files, and linting my code. A few months ago, Ben Alman introduced Grunt, a build tool written in JS and designed for JS. I quite liked the idea of using JS for task automation, since it's a language I already know (bash scripting makes me want to bash my head against the wall) and it's the language that I write my apps in (at least the front ends). I finally got the opportunity to properly try it out last week, while porting my HTML5 slide editing app over from Closure to Bootstrap.

Grunt is designed primarily for JavaScript projects, like Node.JS or jQuery plugins, so it comes out of the box with tasks for concatenating files, linting JS files (via JSHint), compressing JS files (via UglifyJS), running a Node server, and running JS unit tests — no CSS-related tasks. For this web app, I decided to write the CSS using Less, a CSS pre-processor. While debugging locally, I include the .less file on the page and the less.js pre-processor, but for performance reasons on production, I wanted to just include a minified CSS file. So I needed 2 new tasks: one for processing the .less, and another for minifying the .css. Grunt has a formal way for developers to define and share new tasks via extensions, and luckily I found developers have already made grunt-less and grunt-css extensions.

To use the Grunt extensions, I first had to install them as node modules. I could have installed them simply by running npm install grunt-less inside my project folder, but at the suggestion of Ben, I instead created a package.json file with the modules listed as dependencies. Then I just ran npm install and it grabbed everything necessary. Now, since my code is open-source, I don't have to tell people what they need to install - just have to make sure they run install. Here's what my file looks like:

{
  "name": "SlideEditor",
  "version": "0.0.0",
  "dependencies" : {
    "grunt-less":  ">0.0.0",
    "grunt-css":   ">0.0.0"
  }
}

Once I did that, I just loaded the task definitions in my grunt.js file by sticking these lines in:

  grunt.loadNpmTasks('grunt-less');
  grunt.loadNpmTasks('grunt-css');

And now I could use the tasks! However, before I set them up, I spent a while figuring out the best directory structure for both my JS and CSS. When you're using any sort of build tool, it helps to have a sensible directory structure, like separate folders for the debug files versus the build files. For this app, I decided to put all the original files in a src folder with child folders for css and js, and for the build files, I just put them in css and js folders under the root. In the src/css folder, I have children folders less (for the .less file), app (for the processed less file), and libs (for any CSS files from 3rd party libraries). Here's what that looks like:

  • src
    • css
      • less
      • app
      • libs
    • js
      • app
      • libs
  • js
  • css

Okay, so finally, I put the Grunt extensions to work, setting up the less and cssmin tasks. Here's what all the CSS-related tasks look like in my grunt file:

  var SRC_CSS   = 'src/css/';
  var BUILD_CSS = 'css/';
  
  grunt.initConfig({
    // ...
    less: {
      css: {
        src: [SRC_CSS + 'less/base.less'],
        dest: SRC_CSS + 'app/base.css',
      }
    },
    concat: {
      css: {
        src: [SRC_CSS + 'libs/*.css',
              SRC_CSS + 'app/*.css'],
        dest: BUILD_CSS + 'css/all.css'
      }
    },
    cssmin: {
      css: {
        src: '',
        dest: BUILD_CSS + 'css/all-min.css'
      }
    },
    // ...
  });

You can check out my full grunt.js file here, to see how I use the CSS tasks and JS tasks together. Like any tool, Grunt takes a bit of time to learn, but at least for me, I find it much more approachable than the world of bash scripting. Try it out for your next project!

Tuesday, May 22, 2012

The Best Online Editors for Teaching HTML/CSS/JS

Over the past few months, I've been teaching in-person classes on HTML, CSS, and JavaScript, as part of GirlDevelopIt San Francisco. Along the way, I've experimented with various online consoles and editors, and I thought I'd share my experience with using them for teaching. But first...

Why is an online editor useful?

  • Speed: It's often faster to make changes in an online console and see the result than it is to use a standard code editor, save the file, reload the page, etc.
  • Sharability: An online console often gives you a link to your code, which you could easily share with students -- or they could share that link with you (like if they worked on something outside the class and had a question). Look, ma, no upload!
  • Compatibility: An online console works anywhere that you have a browser (and works pretty much the same everywhere), whereas most code editors are specific to the OS and each works slightly differently. By using an online console, all students can have the same experience. Plus, if a student doesn't have a laptop with them (like they only have a ChromeBook, iPad, or iPhone), then they can always use the online console.

That being said, there is a danger to using online consoles — they often work a bit differently than the real thing (saving a file on the desktop + viewing in the browser), which can lead to debugging peculiarities (like line numbers not actually pointing to the right line of code or an inspected source showing an iframe) that can confuse students even further. For teaching and learning, the best consoles are the ones that are as close to the real thing as possible.

JavaScript Consoles

For the JavaScript class, we started off with a few weeks of just JS: variables, functions, conditionals, etc. To make sure the students focused on JS and not how it interacts with the DOM, I wanted them to use JS in a pure JS console. There are many JS consoles out there, but the only one I truly liked is repl.it. It's a console for a variety of languages, and is the one used by Codecademy labs. Here's what I liked about it:

  • It evaluates the JS on the client, so it's fast. (A few consoles send it to a server first, ick).
  • It has a multi-line console on the left-hand side, so you can write lots of code and then run it. Many JS consoles are only single line input, or multi-line if you press shift-enter — but I really wanted multi-line to be the default.
  • It uses the open-source Ace editor for the multi-line console, and that editor includes line numbers and error markers next to problematic lines.
  • It's linkable and printable.
  • It's an open-source project, and the contributors were very responsive to the issues I filed.
  • It works on ChromeBook and to a reasonable extent, iPad and iPhone (yes, one of our students coded on an iPhone!).

HTML/CSS/JS Editors

Once we got to the DOM part of the JS class, I looked for an online editor that supported HTML/CSS as well. In this case, though, I encouraged most students to use their desktop code editors, as they're generally better and they are what they would be using outside of class anyway — so I mostly looked for online tools that I could use for in-class demonstrations and as backup options for students that couldn't use desktop editors.

I ended up using Cloud9 IDE most of the time. Cloud9 a startup for doing web development in the cloud, and their editor is the open-source Ace editor (same as repl.it). The experience of using it is similar to the experience of using a desktop editor - you start a project, create a new file, save the file, view the file in a new window, save and reload, repeat. My only real qualms with it are that it takes time to get started and the project-based interface is a bit much for someone just playing around - and it suffered occasional server issues.

I also occasionally used JSBin v3, an open-source editor from Remy Sharp, based on the CodeMirror editor. I like how lightweight the interface is, but I didn't like how the tool changed my HTML (inserting comments, script tags, etc), and I found the save-view-reload interface a bit confusing. That said, Remy is receptive to feedback and says that I could set up custom templates for teaching. I would definitely look more into it for next class.

Many people would recommend JSFiddle, but I don't actually think that's optimized for learning and teaching the basics of HTML/CSS/JS. It's better for putting together quick demos and test cases, particularly when using libraries.

Wednesday, May 2, 2012

Triggering Numeric Keyboards with HTML5

In the PhoneGap-powered mobile apps for EatDifferent, there's a part where you can enter measurements like weight and body fat — numbers like "12" or "150.5". I realized that I had to switch between the normal keyboard and numeric keyboard (the one with symbols and numbers) whenever I entered measurements, and I wanted to see if I could trigger the numeric keyboard by default for those input fields. HTML5 inputs support a few interesting new attributes, like type="number" and pattern="regex", and I figured I could use some combination of them to get what I wanted. So I started experimenting...

Using type="text" with pattern

The iOS developer docs actually include a section on how to trigger different keyboards and they recommend using type="text" with the specific pattern of either "[0-9]*" or "\d*" to trigger the numeric keypad, like so:

<input type="text" pattern="[0-9]*">

Unfortunately, when you start typing in that field on iOS, it will actually bring up the keypad that only has numbers, not the keypad which also includes symbols like the period, necessary for decimals. And on Android, you just get the normal keypad.

So then I tried setting that pattern to what I actually wanted to accept - numbers and periods:

<input type="text" pattern="\-?\d+(\.\d{0,})?">

On both iOS and Android, that just resulted in the regular old keypad showing up. No good!




Using type="number"

After deciding that Android doesn't pay attention to pattern at all and that iOS probably hard-coded the special case for the strict numeric patterns, I tried using input type="number" instead, with a step value of "0.01":

<input type="number" step="0.01">

Success! On both iOS and Android, a keypad with both numbers and a period shows up.



Now, I re-use my mobile web HTML for the website as well, so I needed to make sure that this solution would work well in desktop browsers. In older browsers, new HTML5 input types will just fallback to text, so we don't have to worry about them, but some newer browsers have special treatment for them, and that's what I needed to check. As I discovered, Chrome actually renders a spinner control inside the input for changing the number value by the specified step value. Maybe it's just because I'm not used to it, but I don't like that interface much, and it doesn't feel appropriate for my use case.

Fortunately, with a bit of Webkit-specific CSS, we can hide the spinner (kudos to StackOverflow for this):

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}


Future-proof?

So, it's pretty cool that we can start being more semantic in HTML5 about what our inputs accept - and that browsers react to provide better input interfaces. But, it feels like it's still quite ill defined how browsers should react, and whether they will all react the same way, and whether their reactions will actually be appropriate for the variety of use cases out there.

Now that I've made this change to a more specific input type, I'll need to be on the lookout for browser changes to make sure it's always the best solution. It's not a change I'd make willy-nilly.

If you've done any experimenting with the new HTML5 input types and keyboard interfaces, I'd love to hear your thoughts in the comments.

Tuesday, May 1, 2012

Learning to Code in SF

San Francisco is filled with developers — but it's also filled with people that want to learn how to be developers. Thankfully, there are many ways for would-be developers to learn from the current developers here, and since I'm getting asked this question more and more these days, I thought I'd share a list of the ways I know. Let me know if I missed anything!

For Everyone


For Women

There are many groups that offer classes solely for women, to provide a more comfortable environment than the usual male-dominated ones (sorry, dudes, but sometimes it sucks to be the only girl in the room).

  • GirlDevelopIt: This is a global organization that offers low-cost web development classes for women, and we're just getting it started here in SF. So far, I've taught an HTML & CSS class for it and am looking forward to the JavaScript class in May.
  • WomenWhoCode: This group was started to bring together women who already code, but just this week, they started holding a weekly frontend web development meetup for women that want to learn more about the various aspects of web development.
  • PyLadies: Dedicated to getting more women in the Python community, the SF chapter of PyLadies is just getting started, and they're kicking off with study groups and hackathons.
  • RailsBridge: Started several years ago with the goal of getting more women in the Ruby community, they organize weekend workshops on Ruby and Rails. Sign up for their mailing list to find out when the next one is happening in SF.
  • HackstarAcademy: An intensive 10-week programming class for women, $6000 but scholarships are available.

For Trans People

There's a growing number of folks that identify as trans in SF, and now there are even coding classes targeted at them - well, one, at least.

  • TranscodeSF: A year of twice-weekly programming classes.

For Kids

Children are our future! And they're also really fun to teach, and can be amazing quick learners. Watch your ego melt away when a kid learns from you and then zooms past. :)

  • CoderDojo: A global organization that puts on free coding workshops for kids, the SF chapter is now hosted at the Twitter office.
  • BlackGirlsCode: Specifically aimed at teaching young women in color how to code, they put on weekend workshops on programming and web development.
  • CodeEd: Specifically targeting girls from underserved communities, it teaches web development skills.
  • WizBots: Using LEGO robotics to encourage kids in math and science.
  • Eureka: A program for teenage girls to get into math and science.

As you can see, there are a lot of opportunities for learning to code in SF - and I didn't even list the many local community colleges with low-cost classes. If you're a developer and want to teach, many of those groups are looking for volunteers. Good luck!