Archives for category: distributed teams

Over the last few years, Liza and I have had the pleasure of building an ever-expanding engineering team. We’ve managed to find great people from across the country and were 100% distributed & office-less until a few folks moved into the new Safari office in Boston two months ago. Because our team was remote by rule rather than by exception, we’ve been forced to develop a culture that exploits new tools whenever they can help us cooperate and collaborate. One particular habit we’re fond of is running meetings through Google Docs.

As is typical with developers, we are not generally fond of meetings, especially recurring meetings, so we have tried to distill them into their productive, fundamental essence1. While our meetings are still far from perfect, I think we’ve developed some conventions worth sharing with other distributed teams.

One minutes to rule them all—in real time

Google Docs sometimes makes it too easy to create and share new documents, so the first lesson is to fight against this: use the same Google Doc for the same meeting week after week and write it during the meeting. This practice is laughably simple, but it removes the biggest threats to useful minutes:

  1. the attendees (claim) to not know where the minutes were/are
  2. the minutes feel worthless because they’ll be ignored forever after
  3. the attendees (wrongly) feel that someone else will write the minutes
  4. the attendees claim the minutes did not accurately capture the discussion

To set this up, just use an extremely clear title for the document (“{Team} {Purpose} Rolling Minutes”) and then add the new minutes at the very top for each meeting. Make it clear from the very first meeting that everyone is expected to help write the minutes in real time (plant some willing collaborators beforehand, if necessary).

It’s worth rotating to a new document every 6–12 months or Google Docs will be crashy.

Collect agenda beforehand (the “Pending” bucket)

Once you’ve established that the same document will be used for each meeting in the series, it is time to start turning the rest-of-the-week time into a lever that makes the meeting itself shorter. We keep an (empty) bulleted list under a Pending heading at the top of every minutes document. The Pending list gets filled by folks as something occurs to them throughout the week. In more extreme cases, the Pending list must be filled beforehand or the meeting itself is summarily canceled (depends on the meeting). Developing the Pending list asynchronously can also make it easier for less outspoken people to make sure their topics get some space in the larger forum.

Establish a repeating structure

While it’s easy to screw up, a carefully crafted meeting structure can help everyone understand when they’ll be actively participating and when the thing is nearly done. The problem is that you have to frequently evaluate the structure to make sure it still actually helps the team communicate rather than being wasteful boilerplate.

Our “big” meeting looks like:

PREVIOUS ACTION ITEMS
(social pressure to finish what you promised)

CURRENT WORK
(*extremely* short Before/Now/Next updates from each person
 don't skip this, as it gets every single human to actually say words at each meaning, 
 forces people to write down what they did [more social pressure],
 and establishes a basic record of what the team was doing in any given month)

DISCUSSION
(a heading for every item that was on the Pending list, plus anything emergent)

ACTION ITEMS
(every time a meeting ends without meaningful tasks assigned to specific humans, a kitten loses its wings)

A repeating structure also helps answer questions about who promised what. You just go down far enough to find it in the expected place in the minutes of a previous meeting. While this is a simple act, doing it consistently makes it clear that the minutes serve a purpose and that the each member of the team is accountable.

Force collaboration and attention through humor

The biggest benefit of cloud-based minutes is the opportunity to use a meeting as a way to help the group gel a tiny bit more, week after week. For distributed teams, the chances for true collaboration and team-building are already extremely limited, so we take whatever we get. Specifically, I want to use the minutes as a tool to have the team:

  • see each other actually (visibly) contributing to a shared project
  • laugh with each other
  • pay attention

A screenshot of a Google Docs document with humorous images and silly fonts

To achieve these goals, we need only two things: silly cat pictures, collaborative authoring. When the team knows that their colleagues are humorously defacing/lolcatting their section of the minutes, ignoring the document is nearly impossible (we don’t actually use Comments in minutes as much as you would expect). Juxtaposing the boss’ description of a particularly rough moment in the release process with a sad panda provides a rare moment to let off steam for people who almost never see each other face to face. And watching a document being written and edited by 5–10 people at once is really quite enchanting.


1 Andrew, our CEO, bought a huge stack of these and forced us to read them, before, ahem the next meeting.

Much of the early part of my career in publishing was spent writing and maintaining scripts used for automating book production. Applying the tools that developers use to manage code to managing manuscripts was a big part of the strategy behind the XML publishing toolchain I helped build (alongside @abdelazer) at O’Reilly (work now being impressively extended by Adam Witwer and Sanders Kleinfeld).

These days I spend much more time in spreadsheets and video conferences than in Vim, but I’ve never lost the appreciation for quality tools. I rarely write my own anymore, but here’s a list of tools I use every day that are indispensable for effectively working with a growing company that has team members around the world:

Google Apps for business

Get your discussions out of email (or worse, email attachments)! @liza and her team introduced us to using Google Docs for tracking running meeting notes, and from there our use of shared documents for everything from meeting agendas to press release reviews to training videos to board reports has grown dramatically. Some days the volume of documents getting updated feels overwhelming — until I remember that the volume of conversation hasn’t actually grown, it’s just been pulled out from where it was hiding in email. The full Google Apps suite is cost effective, easy to administer, and makes it simple to integrate authentication with third-party and internal apps.

Google Hangouts are a simple and easy alternative for video conferencing and screensharing — and Google Voice is a lifesaver for anyone who gets a lot of unsolicited sales calls.

Bonus tip: Comment notifications. It’s not well-documented, but you can use the Google plus “+” convention (or the Twitter “@“ convention) to add notifications to Google Docs comments. For example, if someone includes “+andrew@safaribooksonline.com” in a Google Doc comment, I’ll get notified by email.

Gmail keyboard shortcuts

I’ve used Outlook, Thunderbird, and Mail.app over the years, but I’ve never been faster than when using Gmail right from Chrome — because of the extensive keyboard shortcuts. In general they mimic the Vim convention of modal editing, so they don’t require a lot of complex key combinations. To see the keyboard shortcuts from Gmail, just press “?” (that’s “Shift-/”) while your cursor isn’t in a text field and you’ll get a popover.

SaneBox

The concept is similar to Google’s Priority inbox, but the results are much better. SaneBox connects to your social media accounts to help it filter your email. It works great with Gmail, and is compatible with most IMAP-based email systems. My favorite feature is a folder called “BlackHole” — drop an email in there, and you’ll never see another one from that sender again.

Boomerang

The one feature I missed most from Outlook and Thunderbird when using Gmail via Chrome was the ability to schedule a message for later, so I was thrilled to find Boomerang. Boomerang’s main feature is to send an email from your inbox back to you later, but I use it almost exclusively for scheduling messages to send in the future. Unlike with Outlook, you don’t need to have your computer on for the message to go out. I often catch up on email in batches during odd hours, and it’s nice to be able to queue up replies for Monday morning while working on the weekend, for example.

1Password

While I’m not quite ready to compare them to polio, passwords are a constant aggravation. Having strong, unique passwords for everything and not having to remember any of them is wonderful. 1Password stores credit card info and other info (securely), and syncs with Dropbox to your phone. The Mac version is $49.99 and worth every penny (as is the $14.99 extra you’ll pay for the iOS version).

TextExpander

Expanding short abbreviations into longer text snippets sounds like a small thing, but it makes a big difference over time. Email salutations, phone numbers, addresses — it’s the closest thing I’ve found to Vim’s imap feature for use with everything else.

Alfred

I tried Quicksilver a number of times, but couldn’t get myself over the learning curve. Alfred is similar, and I use it dozens of times a day for launching apps, looking up contacts (the PowerPack is worth the investment), and even doing quick calculations.

CloudApp

A theme with the tools on this list is that most of them only do one thing (or a small number of things) but do them very well. This last one is perhaps the best example of that. CloudApp puts a small icon in your menu bar. You drop files (or folders) onto that icon, and CloudApp creates a shareable link and puts it on your clipboard. I’ve been thwarted enough times by overzealous corporate IT departments with excessive restrictions on file attachment types and size that this is a very worthwhile preventive measure — since you can just send a link instead of an attachment.

There’s a lot more great apps on my laptop and my phone, but these are the essentials I depend on day-in and day-out to help keep the small stuff from getting in the way of the important stuff. Leave your own suggestions for indispensable tools in the comments below.

Hurtling down I-25 at 70 miles per hour, a phone latches securely into its windshield mount, tapping the van’s electrical system to augment its small reserve. Many devices look up as it passes by, sensing a potential WiFi partner, but its transient signal is gone before a pairing can be made. A device on the same trajectory reaches out and shakes hands. A connection is made.

My son looks up from his notebook, “Ok mom. I’m connected.”

“Log in for your class or you’ll be late.”

“Yes mom.”

In London the teacher sits chatting with some early arrivals as she prepares to begin class. “Hi Micah,” she says as he enters the room. In two cities nearly half a world apart a student and teacher connect.

In Colorado Springs, Micah turns to this week’s chapter of “A Tale of Two Cities.” He’s not very fond of Dickens, but then how many 13 year-old boys are?

When I left a stable job of 12 years in south-east Michigan, I had no idea how transient our family would become. The economy took a nose-dive and the job market destabilized. Four years, six jobs, and five states later my wife and I are very glad we chose to homeschool after our first move. But what we have found is that we need help.

In many ways technology has served to separate people from each other. In the distant past people rarely left the town they grew up in. Most people I meet in Colorado were not born here, but have left their roots in search of work or pleasure. Technology has destroyed distance and increased distance at the same time.

But a brave new world lies before us. While several hundred years and a revolution separate us and our distant cousins in England, my son now chats with a teacher there in a way that would have been impossible 10 years ago. The world of online learning is a very different place than it was even four years ago when my family started this journey.

I’m just one engineer working on one small piece of learning technology. But I’d like to think I’m contributing to a revolution.

When Micah gets home I ask him what his teacher gave him for homework.

“I have to write a descriptive essay about a mango or a piece of fruit.”

Sis replies, “Maybe you should write a descriptive essay about dad’s nose.”

That would be a long tale indeed.

Usernames and passwords are lame. Everything that makes them lame on the wider web makes them doubly lame on your company intranet. Here’s how we stopped writing password reset forms.

At Safari, we’ve been trying to make it easier to prototype little applications to show to our colleagues. At the start of the year, we also switched the entire company to Google Apps for Business. While this has mostly been a win for our IT staff and coworkers (with some major exceptions), the range of APIs and developer-focused services provided by Google is tremendous. In particular, it is incredibly straightforward to wire together Django, the django-social-auth package, and Google’s OAuth 2.0 identity service to create secure web applications that are only available to our colleagues. And no more password forms.

API Credentials

The first step in using Google to manage your identities for your Django project is getting API credentials from Google:

  1. Sign into your Google Apps account in your browser
  2. Visit https://code.google.com/apis/console#access in the same browser
  3. On the left menu, Create a new Project
  4. To start, you don’t need any Services, so select the API Access tab rom the left menu and “Create an OAuth 2.0 client ID…”
  5. Fill out the Client ID form for a “web application” and use localhost:8000 as your hostname

Now that you have API Access, you need to Edit settings for the new “Client ID for web applications” you just created. Specifically, you need to enter new “Authorized Redirect URIs” (one per line):

http://localhost:8000/complete/google-oauth2/
http://{dev server}/complete/google-oauth2/
https://{prod server}/complete/google-oauth2/

These are the URLs that Google will return the user to after they have authenticated. Omit the dev server and prod server if you don’t yet know them.

Next, we’ll use those credentials to setup the django-social-auth package, so keep this page open.

Using django-social-auth

django-social-auth is a great package for getting started quickly, in part because it supports a wide range of services out of the box and also because it has detailed documentation.

After you’ve installed the django-social-auth package inside your virtualenv, you need to follow the basic configuration instructions for your Django project. In your settings.py, make sure 'social_auth' is in the INSTALLED_APPS and then run ./manage.py syncdb to get the new tables that django-social-auth requires.

You will also need to add a few more things to settings.py:

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Django Social Auth Config

AUTHENTICATION_BACKENDS = ( 
    'social_auth.backends.google.GoogleOAuth2Backend',  # putting this 1st means that most users will auth with their Google identity
    'django.contrib.auth.backends.ModelBackend',        # ...but this one means we can still have local admin accounts as a fallback
)

LOGIN_URL          = '/login/google-oauth2/'       
LOGIN_ERROR_URL    = '/login-error/'

SOCIAL_AUTH_RAISE_EXCEPTIONS = False
SOCIAL_AUTH_PROCESS_EXCEPTIONS = 'social_auth.utils.log_exceptions_to_messages'  # ...assuming you like the messages framework

GOOGLE_OAUTH2_CLIENT_ID      = 'yourCLIENTidHERE'  # this is on the credentials web page from above
GOOGLE_OAUTH2_CLIENT_SECRET  = 'YOURsecretHERE'    # this is also on the credentials web page from above
GOOGLE_WHITE_LISTED_DOMAINS = ['your-domain.com']  # this is what actually limits access

SOCIAL_AUTH_COMPLETE_URL_NAME  = 'socialauth_complete'
SOCIAL_AUTH_ASSOCIATE_URL_NAME = 'socialauth_associate_complete'

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The most important line from the above is the GOOGLE_WHITE_LISTED_DOMAINS. It’s this setting that limits access to users inside your organization.

Views and URLs

Now that we’ve got auth from Google, we need to wire it up. For a normal application, you’ll want to create a typical view and template for logging in, errors while logging in, a logout view (to delete your cookies), and then ensure you are using the login_required decorator or other access control. For this blog post, I’ll just sketch these out, based mainly off your main urls.py:

...
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import logout
from django.views.generic import TemplateView
...

urlpatterns += patterns('', 
    url(r'', include('social_auth.urls')),                                          # we absolutely need these ones

    url(r'^$', TemplateView.as_view(template_name="login.html")),                   # also fairly important
    url(r'^logout/$', logout, {'next_page': '/'}, name='gauth_logout'),             # this one is nice, but not totally required

    url(r'^login-error/$', TemplateView.as_view(template_name="login-error.html")), # if you've set up messages, you could loop through them here

    # Now we can test whether this stuff works
    url(r'^secrets$', login_required(TemplateView.as_view(template_name="secrets.html"))),  

)

If we set this up and then create a secrets.html in our templates directory:

THIS IS A SECRET!

… and a login.html in our templates directory:

<p>Use your work email credentials to sign in to this application: 
  <a href="{% url socialauth_begin 'google-oauth2' %}?next=/secrets">Sign In</a>
</p>

At this point, you should be able to start the Django debug server with ./manage.py runserver.

Trying it out

If you visit http://localhost:8000/secrets with your browser now, you should be able to try it out. First off, you should not see your secrets yet (even if you are logged in). Before you get there, you need to both be logged into a Google account and grant access to let Google give your identity to this new application. After that happens, Google and django-social-auth will double-check that you are legit and pass the GOOGLE_WHITE_LISTED_DOMAINS. Finally, Google will send you back to the application and in this case you will be redirected to your ?next param.

Do also try it with a personal GMail account to make sure it errors with the login-error.html template. That’s the GOOGLE_WHITE_LISTED_DOMAINS at work.

Gotchas

Well, the most obvious failure is that you use a browser that is logged into your personal GMail rather than your Google Apps for work. The less obvious failure is that this will only work locally if you are running the Django debug server on port 8000 and putting localhost:8000 into your browser (127.0.0.1 won’t work).

It’s probably also worth adding this to one of your loggers inside your LOGGING:

        'SocialAuth': {
            'handlers':['console'],
            'propagate': True,
            'level':'DEBUG',
        }
Follow

Get every new post delivered to your Inbox.

Join 291 other followers