Thursday, January 27, 2011

Actually Healthy Alternatives to Carb Comforts

According to the latest research (and a lot of the books I've been reading, like "The Primal Blueprint" and "Why We Get Fat"), the most likely culprit for obesity and related health problems is carbohydrates.

Unfortunately for those of us trying to get healthy, some of the foods that are highest in carbohydrates are also the foods that are staples of the western diet - bread, rice, and potatoes. If you're anything like me, you grew up on that stuff and can't imagine a life without them. But listen, here's the thing: most of the time that you're eating them, they're just an accessory - it's a holding device for our toppings, it's a convenient scooping device for our tips, it's an absorbing texture for our sauces. It doesn't make sense to consume excessive, unhealthy carbohydrates if they're just there as an accessory.

Thankfully, clever people out there have figured out *actually* healthy alternatives for those accessories, like "cauliflower rice" and "zucchini bread." You can find the recipes for many of the alternatives online, but I discovered them through my new favorite cookbook, Jane Kennedy's "OMG! I can eat that?" I'm making my way through the cookbook now, and thought I would share some of what I've tried so far. Since not all of you will get the cookbook, I've linked each alternative to a similar recipe online. Try them out - you might not like all of them, but atleast you'll be aware that there are less carbalicious options out there for your favorite foods.


If you like... Then try:
Rice Cauliflower Rice
I whip up a batch whenever I've made curry, like the beef/pumpkin one shown here, or my new favorite, Coconut Shrimp Curry.
Mashed Potatoes Cauliflower Mash
I like to eat it with pan-fried steak and asparagus; it's a great texture complement.
Potato Chips Zucchini Chips
I make this as an afternoon snack, and dip them in homemade aioli on the side.
Pizza Crust Zucchini Pizza Crust
We piled our "crusts" high with yummy veggie and meat toppings.
Pita Bread Zucchini Pita
I used the same pizza crust recipe to make pitas for some Lamb Souvlaki.

Wednesday, January 19, 2011

New for Google Shared Spaces: Improved Interface, Inbox, Chrome Extension, & Embedding

Since launching Google Shared Spaces in Labs less than a month ago, we've been making fast iterations on the app - pushing an average of one new release each day.

We purposefully launched Shared Spaces with the bare minimum set of features, so that we could listen to users and developers about what features they most want and need in order to benefit from this app, then implement the most desired features.

Here's a rundown of the improvements we've made - try them out and let us know what you think!



Better, Resizeable Interface

We worked with Cameron Adams, the user interface designer behind Google Wave and cool projects like VisibleTweets, to come up with a sleeker interface for Shared Spaces. We also drew a lot of inspiration from EtherPad, a similar tool for real-time textual collaboration.

Besides subtle style changes, the new interface moves the chat to the right-hand side and adds a full screen button, so users can work on wider width gadgets like the popular ConceptDraw MindWave mind mapping tool.

(For those of you who hadn't seen it, here's a screenshot of the old interface.)



Inbox

We launched with no way of keeping track of your spaces, figuring users could bookmark the ones they were interested in, a la Etherpad. But users (rightly) didn't want to have to worry about keeping track of their space URLs, they wanted to have a list of all their spaces in one place.

At the same time as the interface redesign, we added a universal header with your login information and a "My Spaces" link. That takes you to a list of your most recently modified spaces, with the option to view all of them and the ability to hide any of them. We know it's not a full-featured inbox like GMail's, but we think it's a sufficient way for users to manage their spaces while we build out other features.



Chrome Extension

Personally, I love to use Chrome extensions as a way to keep tabs on what's happening in my favorite communication tools without actually having to open them in a new window - Twitter, GMail, Wave, etc.

So, we created a Chrome extension that both gives you a mini view of the gallery for quick space creation and a list of your most recently modified spaces. When someone modifies a space that you're on, the extension badge changes to let you know.



Embedding

We live in a web where everyone, from developers to users, want to be able to mash their favorite products together. Our users asked us for the ability to embed their spaces in their webpages, on their Google Sites, and in iGoogle.

For the typical case of embedding on a webpage, we launched an embed wizard that lets you tick some options and then generate a JavaScript code snippet (similar to Google Web Elements). For the Google properties (where JavaScript isn't allowed, but gadgets are), we automatically generate a gadget for each space, and we give users the URL of the gadget that they can then insert in their site or dashboard. For either method, users can click "Embed" in the space to get started.

Wednesday, December 29, 2010

Why I Love(d) Wave

Note: I originally wrote this post in August, but decided not to publish it until now. I've left the text as it was then.

As recently announced on the Google blog, Google has decided to discontinue Wave as a standalone project. As many of you know, I worked on Wave for the last year, as an advocate and support engineer for the APIs, and well, as you can imagine, I'm quite disappointed right now.

I know that one of our failings in growing Wave usership was the inability to properly articulate what Wave is good for, and I think part of the reason is that Wave can't be simply compared to any existing service. Wave is a flexible and generic platform, one that can be used for groups discussion, for diary entries, for event commentary, for surveys. Of course, there are existing solutions for doing each of those things - but there is not one solution that does all of them. The thing I loved about Wave is that I could start a wave about a topic, and that wave could evolve from a survey to a discussion to a photo album (like when I asked folks what color I should dye my hair next), or be a combination of all three at once. I didn't have to know at the beginning what it would be, I didn't have to carefully weigh all the different options, I could just start a wave and see what it became. There are of course reasons for using specialized tools, like when you absolutely must have pristine formatting in a Word Document, but when you're collaborating with your group of colleagues or community of developers, I've found it's better 99% of the time to have the flexibility of Wave than the specialized features of 10 different services at once. That's why I will find it hard to stop using Wave (and I hope I don't have to), because I would have to replace it with myriad different tools.

Because Wave is this flexible, we realized from the beginning that it should also be extendible, so that users could customize Wave for their own particular needs, and combine its conversational features with their own processes. Sure, you can do event planning with just text, but throw in a date picker gadget, RSVP gadgets and maps, and you've made planning that much more compelling. You can write your blog post drafts with just the native features of Wave, but after you throw in an approval gadget and blog post publishing robot, you've got a full blog post workflow in Wave, one that can be easily shared with your colleagues.

On the Wave engineering team, we had started to build extensions to streamline our own internal processes. For example, we have a system on the team where a different group of people are on-call each week, and it is their job to respond to the pages and alerts about server issues, and escalate them to other folks if they can't handle them. To supplement this system, we would create an on-call wave each week, and the on-call engineers could paste the alerts into that wave, ask questions about them, and anyone following the wave could discuss the alerts with them. To enhance this system, one of my colleagues wrote a robot that 1) created the new on-call wave each week on a chron job, adding the relevant people based on calendar entries, 2) added links to all the current server admin pages, and 3) pulled the alerts directly into the wave. So, now, we have what amounts to a semi-automated system, where a piece of software does what it's good at (bringing in structured data), and humans do what they're good at (conversing on top of that data). It really shows off the power of Wave to combine structure plus free-form conversation; to combine data plus humans.

In Wave developer relations, we built multiple extensions to enhance our interactions with external developers. For example, we have a gallery of third-party extensions in Wave, and since we wanted that gallery to contain only user-friendly extensions, we created an App Engine app for developers to submit their extensions to the gallery. The app worked okay for storing the structured data about the pending submissions, but it was not conducive to having lengthy discussions about various aspects of an extension. As soon as I got the chance, I moved the process to Wave. Now, developers create a new submission wave, a robot fills it in with questions and fields, they answer the questions and click a button to share it with the review team. We add our own comments, and then click a button that physically puts it into the gallery. With the new wave-based process, we can get into inline discussions about their answers, I can start up private replies with my colleagues to ask for their unfiltered opinion, and we can attach images to show what we see or would like to see in the UI. We ended up with much better extensions, because we were able to have such fast and meaningful conversations -- and we didn't lose any of the structured data along the way.

My job is all about communication and collaboration, and that is what Wave excels in. The more I used Wave, the more I saw how Wave could make my job better, and the more I fell in love with the power and potential of Wave. I hope that the ideas of Wave keep going, whether through other Google products or the ever-growing Wave open-source community, because I don't want to live in a world without them.

Tuesday, December 28, 2010

Triaging Issues: The Wave-y Way

Note: This blog post was originally written to be posted on the Google Wave blog, but did not make it out before the cancellation of Wave. I am posting it here in case it is useful to Wave open-source developers in the future.

In developer relations, one of our core roles is to respond to the bugs and feature requests for our APIs, and we do it both because hearing what developers want helps us shape our APIs, and because understanding the bugs that developers encounter is one of the best ways to deeply understand an API. As a general rule, we try to respond to all new issues within a week of their filing, and we usually accomplish this by holding a weekly triage meeting with colleagues to review new issues. Back when I worked on the Maps APIs, we were all in the same office, so we'd just crowd around a table with our laptops and mash through the bugs. But, now, on the Wave APIs team, my colleagues are spread entirely across the world, with me in Australia and them in both coasts of the United States. So, we needed a new way to triage, and being on the Wave team, we went the obvious route: use Wave!

For our first weekly meeting, we simply met in a wave and pasted the untriaged issues ("Status-New") from our Google code issue tracker into the wave. Then we'd look at the issues and reply beneath the ones we were checking out to lay dibs on them, so we worked on whatever bugs were most up our technical alleys. While we were researching a particular bug and had questions about it, we could start an inline conversation beneath the bug, so in the end, we could both work in parallel and in collaboration. This way, we were able to both triage many bugs and feel confident in our triaging, since we could bounce ideas off colleagues easily during the process.

After the first meeting, I started thinking that I could probably whip something together with our APIs that would streamline the triage process even more, and maybe make it possible for other teams to follow the same process. Around the same time, the project hosting team announced an Issue Tracker data API - perfect timing!

So, with the powers of the Wave Robots API and Issue Tracker API Python client libraries combined, I wrote the Bug Triagey robot. When you add Triagey to a wave, the bot titles the wave with the current date and inserts a configuration gadget. That gadget lets you pick a triage template, or create a new one. In a template, you just need to specify the Google code project, the Status label, and optionally, a label to sort by. For example, my Wave APIs template specifies "google-wave-resources", "New", and "ApiType", as we try to categorize our issues by sub-APIs, and each of us specialize in different APIs.

After you're happy with the template, the bot loads the sorted issues into the wave, and puts dibs buttons under each one, so you can indicate if you're "Looking at" or if you've "Responded to" an issue. After clicking a button, Triagey changes the button to show your username, and after clicking the "respond" button, Triagey strikes the issue out. That way, it's easy for you to review the issues in the wave and immediately see which issues haven't been triaged yet, and who's working on what. And just like before, you can start inline conversations to discuss the bugs you're working on.

Triagey is one of my favorite examples of how you can use Wave to combine automation and conversation to make collaboration easier and more efficient, and of how you can hold multiple structured conversations in a wave. To try it out yourself, install it from this wave. To modify it for your own use (like to triage other items) or to see how it was built, grab the code from the samples gallery.

Using App Engine to Turn Emails into waves

Note: This blog post was originally written to be posted on the Google Wave blog, but did not make it out before the cancellation of Wave. I am posting it here in case it is useful to Wave open-source developers in the future.

When I first discovered Google Wave and started working on its APIs, I realized that Wave had the potential to be a universal inbox one day, using the robots API to collect notifications from all the different inboxes and services that I use. I've since realized that many legacy services don't provide notification APIs but many of them do provide a common integration point - email - letting you specify an email address, and then sending new information to that address. Well, as it turns out, it's very easy to create an app in App Engine that can receive email at particular addresses, and then process that email. Using this technique, I can realize my dream of a universal inbox, by simply forwarding all my services to an AppEngine-powered email address, and appending the email content to a wave in my account.

So, I created the Mail Digester bot to do just that, and open-sourced the code for all of you to base your own mail-receiving robots on. The app starts with a handler for all inbound emails, so it receives emails sent to any [address]@maildigester-bot.appspot.com. It then creates or finds the digest wave for the particular address, and appends a blip with the content of the email, converting any HTML emails to text. For example, if I send an email to "myemailupdates@maildigester-bot.appspot.com", and the app can't find any digest associated with "myemailupdates", it will create a new digest wave and add my wave address. The next time that I send an email to that address, it will find the wave in the datastore, and append a blip to it with the converted email. I wrote it this way so that I could easily create different addresses for different purposes and subscribe to a few different waves.

Once I created the robot, I set to work seeing how many services I could bring into Wave via email notifications. First, I set an address as the issue notification email for the google-wave-resources Google code project, so I could have a digest wave for all the issue updates. Next, I added another address as a member of the Google-Wave-API google group, so I could have another digest wave for new group messages. Finally, I set another address as a Gmail forwarding address, piping all my inboxed emails into a third digest wave. I still need to pop out to Gmail or other services when I need to reply, but in fact, now that I do all my team and community collaboration inside Wave, I find I only need to respond once a day or so.

Using the same principle of using a robot to respond to emails, several of my colleagues have built robots to respond to specific types of notifications. For example, one Wave engineer wrote an on-call robot that parses incoming pages from email and appends them to our weekly on-call wave, so that it's easy for us to discuss the contents of the page. Another food-loving colleague wrote a bot that turns the daily menus into waves, adding images of the food and rating widgets.

Though it at first feels odd to be using email as an integral part of a wave robot, responding to email is actually a great way to integrate Wave with existing services in your organization and even add a collaboration layer on top of them.

Issue Tracking in Wave

Note: This blog post was originally written to be posted on the Google Wave blog, but did not make it out before the cancellation of Wave. I am posting it here in case it is useful to Wave open-source developers in the future.

Like most folks in the software development field, we spend a fair bit of our time on the Google Wave team tracking bugs and feature requests. At Google, we use an internal tool for tracking issues, and that tool lets us set some initial info about the bug, choose its status from a set of options, set its priority level, and assign it to a colleague. From there on out, we can append comments to the issue to discuss it further. The tool works well as a way of tracking the status of bugs across many projects and people, but it doesn't work as well for working through bugs quickly, or for having more complex conversations on requests. On the other hand, Google Wave, works really well for collaborative debugging, since you can easily paste code in, attach files, and comment on each other's code, and of course, it works great have for having conversations that diverge into multiple threads. I really wanted a way to have the best of both worlds: the structured input of our tool, plus the collaborative conversations of a wave. So, we worked with a team to create the Issue Tracky extension to do just that.

When you install Issue Tracky, you'll see both a new item in your "New Wave" drop down and an icon on your toolbar. You can use the former to create a new issue report from scratch, or you can select text in a wave and click the toolbar icon to turn it into an issue. In either case, a robot will fill the wave in with a title and info gadget, where you can pick the issue type, priority level, and assign the issue to a colleague. From there, you can add a description using all the Wave editing tools you're used to, attaching files or pasting formatted code, and then add your colleagues to the wave to discuss the issue.

The robot will automatically tag the wave based on the information in the gadget, so that you can create saved searches for all of the bugs assigned to you (e.g. "type-bug assignee-pamelafox") or all of the high priority requests ("type-request priority-1"), and easily browse through them in weekly reviews or triage meetings.

We've created a generic version of the Issue Tracky tool that anyone can play with, but we've also open-sourced the code, because we think that this is the kind of extension that companies will want to both use and customize for their own use, perhaps by integrating it with existing systems or changing the gadget to collect different pieces of information. To get started with your own version of it, download the code and follow the instructions in the readme. If you have any questions or want to share how you've extended the codebase, stop by the forum.

We hope this helps you have more productive discussions on the issues in your projects, and better collaboration with your teams. Enjoy!

Google Shared Spaces: How We Made It

In my last post, I talked about why we made Google Shared Spaces. Now I want to talk about how we made it, as I think it may surprise a few folks. Much of the press about Shared Spaces claims that it is built off "Wave technology", when in fact, the only piece of Wave technology that Shared Spaces utilizes is the Javascript Wave Gadgets API. Since what we set out to make was really pretty simple, we decided to start from scratch and use a combination of open-source frameworks and publicly available APIs.

We use Python App Engine as the hosting platform, and Django 1.0 for the templates & request handling. We embed the gadgets in the pages using the iGoogle Gadget Renderer (a deployed version of the open-source Shindig server), the gadgets communicate with the page using the Wave Gadgets JavaScript API (now open-sourced), and the page communicates with the server using the Channel API (App Engine's approach to COMET). For AJAX processing and UI features, We use the Google-hosted jQuery. For authentication, we use the Twitter API, Yahoo API, and Google Buzz API. For the comments and ratings in the gadgets gallery, we use Disqus threads.

By starting from scratch while reusing the relevant technologies out there, we were able to build the first version pretty quickly (within a few weeks), and we're also able to iterate quickly now (new releases every couple days). While the open-sourced codebase for Wave is around 250,000 lines of code, the codebase for Shared Spaces is now about 5,000. Wave is a complex technology encompassing multiple algorithms, backends, and interfaces, while Shared Spaces is a user-facing app that uses just one small part of that technology and is developed by a small team. If you pick the right tools for the right job, you can do a lot with a little. :)