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!