Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Tools
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Nina Mehta

What to do with all your data; talking visualization with Sarah Nahm and Ian Johnson

Nina Mehta
Friday, June 14, 2013

“Google search results are data viz,” said designer and coder Sarah Nahm who came to visit the Pivotal Labs design team for lunch in San Francisco. Sarah visited us with her colleague and friend Ian Johnson, creator of visualization environment, Tributary. Sarah and Ian work closely with all kinds of data visualizations projects and visited to help us learn more.

Before getting visual or even making reductive design decisions, Sarah and Ian ask, “what do we want to do with this data?” They’re always asking themselves and their users, ‘what should the data do?’ before getting to its form or structure. You have to look for constraints, they said, and a data visualization is not about putting an interactive chart on a page somewhere. You have to think about what it should do and with what it’s competing with on the page.

Data is information that is available, sometimes ambiently so. A visualization of it “should be a portal to what you’re trying to do.” You want to have the kind of information people need, when they need it, and that’s when they’re happy.

Visualized data is active content, unlike a static site. Buttons need to do things, other than go to other pages. Ian has a hard line about prototyping with fake data. He doesn’t do it.

Clients are not always good at coming up with real data, he said, but you have to try to get it. Without it, the prototypes don’t make sense and people find them confusing because there’s no context. “They need to see themselves in the data,” he said. If he doesn’t have it, he makes very rough prototypes in D3 or relies on paper sketches.

Now more than ever changes with transitions and animations are more important. They make a difference on how it feels when the data changes. “Data is beautiful,” Sarah said, you just have to think about where there’s competition.

Want to learn more? Get in touch with Sarah @srhnhm or Ian @enjalot. They also shared a mountain of wonderful resources linked below. Thanks so much for coming by, you’re welcome back any time!

Blogs

1500 D3 Examples 
D3 + Data Vis with Ian Johnson
Numbers rule your world
Junk charts
Andrew gelman’s blog
Five thirty eight
The why axis
Gregor aische’s blog
New York Times’ documentation of work

Books

Semiology of Graphics (1967) by Jacques Bertin – french data visualization master from the 30s through 90s
Show me the Numbers (2004) by Stephen Few – a guy who’s really into good tables.
Information Visualization (2004) by Colin Ware – goes into the science-y side of perception
Cartoon Guide to Statistics (1993) by Larry Gonick and Woollcott Smith

 

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Matthew Parker

A 1 minute test suite, 6 months in

Matthew Parker
Friday, June 14, 2013

“Rails is slow, but Rails tests are slower.” Rails may be slow, but I’m here tell you that it’s likely you have only yourself to blame for your slow test suite.

I’ve seen some bad test suites in my day. I was once pulled onto a rescue project that had a total build time of over 24 hours. The health and future of your production code depends on a fast, reliable test suite. On my current project, we believe in fast feedback and we’ve focused a lot of detail and attention to our test suite. It’s paid off in dividends.

We’re six months into a project, and we have a one-minute test suite. That includes both rspec and cucumber (and the time to boot up rails for both). Here’s how we’ve accomplished it:

1. Pyramid

Your test suite is a pyramid. Skinny at the top (journey acceptance tests), fat at the bottom (unit tests). Conceptually, this sounds right, and it’s easy to think that it should just naturally fall out of the BDD outside-in process.

But in reality, it’s not a given. Tools like cucumber make it so easy to spin up new high-level acceptance tests for every edge case you can think of. Taken to an extreme, this can lead to unit-testing at the browser level. That doesn’t mean you were wrong to drive out exceptional paths at the acceptance level. But you should ask yourself this question before you check in: “Will I have any less confidance in my test suite if I don’t check in this acceptance test?” If you have already covered a happy path of a feature, and at least one exceptional path of a feature at the acceptance level, do you then need to cover every other exceptional case at the acceptance level? If you’ve added tests down at lower levels in your stack, then you might already have all the verification value you need.

2. Grooming

We groom our test suite. Not every day. But we watch it and keep it in order. We listen to pain in our test suite (“Why did that change cause so many other tests to break? And why did I have to go to every single test and fix each one of them individually?”). When we revisit old tests, we reconsider it in light of what we know about our application. We keep an eye out for duplicate tests.

3. Refactoring

Our fast test suite practically begs us to refactor. And it turns out, refactoring often leads to simpler designs, objects with fewer methods (and therefore fewer tests), as well as objects that are easier and more natural to test in isolation. Great tests will lead you to refactor your production code, which will lead to even better tests.

4. ActiveRecord Containment

How many tests require you to create data in the database? Ideally, that’s limited to just your acceptance tests and your activerecord model tests. But the reality is never that simple. Although it’s technically possible to accomplish this with any Rails application, it’s not always feasible, or even desired. Libraries you use may make this difficult in some circumstances (e.g., devise).

You might also find yourself in a situation where testing an object in isolation would lead to a very brittle test that knows the entire implementation via stubs and mocks. Worse, you may not see a way to untangle these dependencies. That’s OK. Test the object’s behavior, even if it means integrating with other objects. As your understanding of your application’s problem domain crystallizes, and as more patterns begin to emerge, you will eventually find a way to simplify it, to untangle the dependencies.

5. GC.disable

At one point on our project, our build time crept up to nearly two minutes. We saw ourselves slipping down a very slippery slope. We started running it less, refactoring less, and even finding less motivation to keep our test suite clean. So we threw a chore at the top of our backlog to bring the test suite time back down to a minute. Our PM was naturally wary, asking us to timebox the chore to an hour or two, but we made the case that this was of critical importance to the future of the project, and that the pair working on it would check with the rest of the team after a day.

Near the end of the day, the pair stopped and told the team that they thought they had done all they could, but it only got the test suite down to 1:20. Shaving forty seconds off a test suite in a day is a fantastic feat, and when we looked at what they’d done, we were really impressed. I jokingly suggested disabling GC. And then they actually did it. The test suite time dropped to 48 seconds. I’m not recommending you do this. This is a bit of a nuclear option. If this is the first thing you try to improve your test suite time, then you are missing the point of bringing your test suite time down. But if you feel like you’ve done everything you can to legitimately bring down the time of your test suite, then consider disabling garbage collection. Ruby GC is a beast, with the potential of turning a linear algorithm that creates N objects into a quadratic algorithm. Weigh the pros and cons, and decide for yourself if you can live with the dirty dirty feeling this will give you.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Charles Hansen

When pairing doesn’t transfer knowledge fast enough

Charles Hansen
Tuesday, June 11, 2013

Pairing is great for knowledge transfer.  With a reasonably sized 3 pair project, you can completely avoid code reviews, training sessions, tutorials, or any other means of teaching a developer how something works.  I had a good amount of time on a 6 pair project with a ~15 developer-year code base and felt a lot of pain with poor knowledge transfer during that time.  Pairing just wasn’t enough.  We experimented with a few additional ideas during that time and found four that I would certainly use again on any future large project.  The parking lot, a pairing whiteboard, frequent technical retros, and developer documentation.  If your project is big enough, you might want to give these a shot.

Parking Lot

This has been used on a number of other Pivotal projects, but worth reinforcing.  As you are working through the day, if you ever create something other developers should use in the future (a helper method, or useful mixin, or new jasmine matcher, etc), write it on a whiteboard.  This goes double if there is something old that developers should have been using but didn’t know about.  I’ve found I typically write one thing per day.  At the beginning of standup (or perhaps before standup, with only the devs), go over all of the items on the whiteboard.  The same info could be contained in the standup itself, but there is a lot more useful info transferred this way in practice.

Cost:  A few minutes a day, less than the cost of standup.  The ask ticket for the whiteboard.

Pairing Whiteboard

Also taken from other projects.  We found that if pair rotation is basically random, you have very little control over what you work on and therefore very little control over what parts of the code you know.  We have every person’s name on a post-it note and write down every story that is carried over from the day before on a whiteboard with the owner’s post-it next to the story.  This goes for stories that weren’t started the day before but clearly need to be started today.  You then assign pairs based on who needs to learn what.  You can also use the same mechanism to create power pairs if the situation calls for it, but that doesn’t help knowledge transfer.

Cost:  Same as the parking lot, plus some post it notes.

Technical Retros

I used to think technical retros were only when the whole code base is about to fall apart or you need make a major infrastructure decision and want the whole team involved.  Developers know how to solve technical problems and we don’t need meetings to do this. It turns out these retros are great for knowledge transfer.  We only did this for a month or so before our team size was reduced for other reasons, but I’d recommend having technical retros on big teams once a week until you feel they aren’t helping and then dial back.

I have observed three mechanisms for this knowledge transfer.  If someone has been working on a component they don’t understand, this will end up in the unhappy column and now everyone knows that component needs more explanation.  If someone knows a component and thinks it should be changed, everyone else gets to listen in when the two people who actually understand it discuss it.  Lastly, when people discuss changes to the infrastructure, it ends up including good explanation of how the current infrastructure works.

Cost:   An hour-long meeting every week with the whole team is a lot.  Only really worth it on large teams.

Developer Documentation

I’ve noticed we treat documentation at Pivotal the same way we treat code comments, lies waiting to happen.  What is the point of writing down how some API is going to work when you already have tests describing the behavior and the API is just going to change next month?  This may be true for low-level documentation, but we have drawn a lot of value from higher-level documentation, showing general code flow and including examples of how pieces fit together.  We originally wrote the documentation because we are open source and needed to help out some third party devs, but it turned invaluable as a teaching resource.  Our documentation is split into two parts, one with info on how the code works (here) and a second “DevOps Manual” with info on dealing with our development and testing setup.  The DevOps manual is in a private repo.

Documentation writing was prioritized in tracker.  There were many stories “Document X” that built things up over time.  The stories were usually the result of action items from retros (technical or process) whenever a knowledge silo is identified.  Many pages were written by a pair with one experienced person and one new person.  This has the benefit of teaching the new person and forcing the experienced person to write something a new person can understand.

None of this addressed the ‘lies waiting to happen’ problem of documentation.  This is why we go over it all with each new developer on the project.  After a new developer has had some time to get their bearings, we put a chore in tracker to go over documentation with them (only if you are pairing with them of course).  The other part of that chore is to fix anything that is wrong.  This way every developer gets a baseline starting point and the documentation stays up to date.

Independent of knowledge transfer, the DevOps manual is pure gold.  It pays for itself as soon as something like “The VM running my third party database for development ran out of hard drive space and corrupted the database” happens more than once.  Whenever you fix something like that, even if you think it can’t possibly happen again, write it down.  When you set up that VM in the first place, write that down too.

One last note on documentation, even if you don’t use actual documents, it helps a lot to document in the code itself.  Detailed commit messages, detailed descriptions in tests, even code comments themselves occasionally.

Cost:  We have probably spent at a pair week or two writing our docs.  Maybe not worth it on a small project, but not too painful.  The DevOps manual itself would still be valuable on most projects.

 

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Stephan Hagemann

Getting “by” with rspec feature specs

Stephan Hagemann
Monday, June 10, 2013

If you find that you are making your rspec feature specs longer and longer to cram more coverage in or to prevent the setup costs for more, shorter tests, you will probably have found that it becomes very hard to figure out what’s going on. If you find yourself wanting to add comments to your specs to state what is happening, like so:

it "allows the user to manage her account" do
  #Login user
  visit "/login"
  fill_in "Username", :with => "jdoe"
  fill_in "Password", :with => "secret"
  click_button "Log in"

  expect(page).to have_selector(".username", :text => "jdoe")

  #Account management
  click_link "Account"
  expect(page).to have_content("User Profile")
  ...
end

Why not throw this code into your spec_helper.rb…

def by(message)
  if block_given?
    yield
  else
    pending message
  end
end

alias and_by by

… and replace the above section with this:

it "allows the user to manage her account" do
  by "logging in the user" do
    visit "/login"
    fill_in "Username", :with => "jdoe"
    fill_in "Password", :with => "secret"
    click_button "Log in"

    expect(page).to have_selector(".username", :text => "jdoe")
  end

  and_by "managing the account" do
    click_link "Account"
    expect(page).to have_content("User Profile")
  end
  ...
end

Instead of having comments, which I personally am trained to a) ignore and b) expect to be outdated, I now have a block which denotes that a part of the spec as doing something specific. I like to think that the benefit of the block over the comment is that

  • it has a start and an end (and thus adds programmatic structure),
  • it adds indentation (and thus visual structure),
  • it can have behavior (like the use of rspec’s pending).
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Andrew Bruce

I’ll come running back to GNU

Andrew Bruce
Sunday, June 9, 2013

I’d finally had enough of waiting for my MacBook Pro’s hard drive platter to spin up and grind away, so I ordered a new Solid State Drive. When it arrived, I was faced with the potentially drawn-out process of setting up a new OSX installation, which might have involved proving I owned a copy of Lion or whatever beast I currently had installed. Alternatively, I could have cloned my old disk, but I’d heard that was sometimes problematic, and the fresh disk felt like an opportunity to wipe the slate clean. I chose what I thought must by now be a path of less resistance for many developers: installing Linux instead.

This wasn’t a wild jump into the unknown: I had run various flavours of GNU/Linux since I was a teenager, eventually settling on Ubuntu. I bought a dedicated Ubuntu laptop from Dell when they came out, but caved and bought this MacBook Pro when I could afford it, around 2008. Persuading myself that I needed to run Photoshop for my fledgling freelance career, it struck a good balance between the benefits of the open-source community and the plug-n-play reliability of a popular commercial OS. Development on OSX is pretty easy these days, but there are still some weird issues. Installing Ruby on OSX was non-trivial last time I tried, homebrew eases some dependency installation pain but introduces its own set of maintenance chores, and Xcode is a bit of a dog to keep up to date.

There’s some conventional thinking in the Linux desktop world that says old hardware has a better chance of working. The MacBook Pro 5,5 that I have is a recognisable set of venerable components (“sudo dmidecode -s system-product-name” gives you the version of the Mac hardware you are running). However, the latest Ubuntu 13.04 sets things up so that I:

  1. Am greeted with a corrupted boot screen each time I start the computer
  2. Can turn my machine into a brick by simply closing the lid
  3. See a long horizontal shadow at the top of full-screen videos
  4. Am regularly thrown into a different app when I use two-finger scrolling

Granted, this is a far cry from, “my wifi and sound took three weeks to set up and still didn’t work well”, but it’s hardly the smooth experience that Mac users are used to. Here are the fixes.

Corrupted boot screen

Use proprietary nvidia drivers. These work better, but you’re then stuck with a permabright, battery-sucking screen because the keyboard brightness controls don’t work. This can be fixed by getting your hands dirty in a config file (note the older version of Ubuntu this was solved for). Not awesome.

Brick state

This happens because a lid close defaults to suspend, and suspend breaks the open source nvidia drivers. A reboot doesn’t seem to fix it. Kudos for getting so far with reverse engineering, but I wasn’t prompted with an option to choose the proprietary drivers. I’d rather check a box that says “non-free software that works” over “free software that ruins your day”. This prompt used to be present on previous versions of Ubuntu.

You can sometimes recover from the brick state by moving to a regular non-X console (ctrl+alt F1) and killing lightdm with sudo. Otherwise, you’re in boot-from-CD land. How many non-technical types do you know who can / would want to work that out?

Horizontal shadow of panel when running videos full-screen

This one is interesting. The Ubuntu team knew about the problem, but shipped anyway. I can sympathise with the difficulties of maintaining a dual server and desktop OS, and with sticking to a deadline, but if the 80% case could have been solved by loading Workspace Switcher by default, why not do that?

Two-finger scrolling occasionally throws you into another app

This happens because Unity has hardcoded gesture support for touchpads. Three finger double-tap means ‘take me to the previous window’, and three finger tap then tap-and-hold means ‘open a super-sensitive window switcher that I control with the mouse’. I never want to change windows with the mouse.

There are several solutions to this problem floating around, but they all involve modifying the Unity source code and recompiling. This is crazy. Please make gestures optional!

In summary

I had previous experience with dealing with this category of problems, but someone who isn’t a full-time computer nerd might not have so much fun dealing with these bugs. Many of them seem to be reflective of Ubuntu’s confused attitude towards free software. The OS seems to push free drivers onto the user, but they aren’t always ready for prime time. Are end-users being used as testers?

Perhaps I’ll give it a few weeks before I cave and reinstall OSX. Most of the creases are ironed out now, but if I have to spend another few hours googling for solutions to mindless bugs, I may just give in to temptation again. YMMV.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Luan Santos

Ember.js and Flot charts

Luan Santos
Sunday, June 9, 2013

Update (Jun 10th, 2013): Use custom view helpers and view bindings to make it simpler and more reusable.

Continuing to play around with Ember.js I wanted to draw some charts. I’ve used flotcharts with great success in the past but since it’s just a jQuery plugin it is obviously unaware of ember’s amazing data binding capabilities.

Flot requires you to pass in an array of datapoints every time you want to create or update a chart. You can’t use a bound variable in that array so we will have to use observers to manually update flot when our data changes.

Let’s create a flot view that we can re-use in our templates:

App.FlotView = Ember.View.extend({
  classNames: ['canvas'],

  didInsertElement: function() {
    this.renderChart();
  },

  chartUpdated: function() {
    this.renderChart();
  }.observes('datasets'),

  renderChart: function() {
    var canvas = this.$();
    if (canvas) {
      canvas.plot(this.get('datasets'));
    }
  }
});

We’re assuming here that this view is being added on a context where the controller has the property datasets. We observe that property for changes and we ask flot to update the chart accordingly. Here’s how you add that in your template:

Here we pass in our controller’s property datasets. We observe that property for changes and we ask flot to update the chart accordingly. Here’s how you add that in your template:

<script type="text/x-handlebars" id="charts">
  {{view App.FlotView datasetsBinding="datasets"}}
</script>

You can go further and make it cleaner by defining your custom view helper:

Ember.Handlebars.helper('piechart', App.FlotView);

And use it as simple as:

<script type="text/x-handlebars" id="charts">
  {{piechart datasets=datasets}}
</script>

For a simple line chart that datasets property should be something like an array of series [[[1, 3], [2, 14.1], [3.5, 3.14]], [[1, 3], [2, 8], [3.5, 2]]]. Refer to the flot documentation for more details on that.

Let’s say we have a controller like the following:

App.ChartsController = Ember.ArrayController.extend({
  datasets: function() {
    return this.map(function(chart) {
      return chart.get('dataset');
    });
  }.property('@each.dataset')
});

Here we’re creating the datasets computed property that depends on @each.dataset, that is, if the dataset property changes in ANY of the underlying models associated with this ArrayController this property will be updated and the observers (as the one we defined in the view) will be notified.

The dataset property on the model could be returning either one of the mentioned series or an object with more detailed information such as labels, color, etc..

That’s all the non-standard code you need for it to work, everything else is regular models and routes, the result is pretty nice and very responsive. You can also have your models pool for server changes for an auto-updating chart, and it’s definitely a very clean and straightforward implementation for something that would require quite a few events and callbacks if we didn’t have the sweetness of databinding.

I’ve published the complete example using pie charts (their datapoints are simpler) on this JS bin.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Jared Carroll

Working with RubyMine’s Tabs and Splits

Jared Carroll
Sunday, June 9, 2013

RubyMine’s editor is tab-based. When you open a file, it’s opened in a new tab. The editor can also be split vertically or horizontally; allowing you to edit multiple files simultaneously. In this post, we’ll look at managing and navigating these basic RubyMine concepts on OS X.

Navigating Between Tabs

Move between multiple tabs with command + shift + [/] or control + Left/Right.

tabs.png

Closing Tabs

Close a tab with command + W.

There are several other useful tab closing commands that don’t have keyboard shortcuts. I add the following shortcuts for them:

  • command + shift + W – close all other tabs
  • command + option + W – close all tabs
  • command + option + control + W – close all unmodified tabs

Splitting the Editor

Edit multiple files simultaneously by splitting the editor using the “Split Vertically” or “Split Horizontally” commands (find these commands quickly with command + shift + A, “Find Action”).

split-editor.png

I add command + option + control + Up/Down shortcuts to split vertically and horizontally.

Navigating Between Splits

Move the cursor to the next split with option + Tab. Move to the previous split with option + shift + Tab.

The Switcher can also be used to move between splits. Use control + Tab to open the Switcher, continue holding down control, and then use Tab to select an open file to navigate to.

Moving Tabs Between Splits

Use the “Move To Opposite Group” command (find this command quickly with command + shift + A, “Find Action”) to move tabs between splits.

I add command + option + control + Left/Right shortcuts for this command.

Closing Splits

Close a single split by closing all of its tabs.

Close all splits with option + shift + X.

Master the Basics

In RubyMine, you’ll use tabs and splits all the time. Don’t hesitate to create custom shortcuts for tab and split commands that don’t have them. Efficiently managing and navigating tabs and splits from the keyboard is a fundamental, must learn RubyMine skill.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Amit Gupta

So You Still Don’t Understand Hindley-Milner? Part 3

Amit Gupta
Saturday, June 8, 2013

In Part 2, we finished defining all the formal terms and symbols you see in the StackOverflow question on the Hindley-Milner algorithm, so now we’re ready to translate what that question was asking about, namely the rules for deducing statements about type inference. Let’s get down to it!

The rules for deducing statements about type inference

Read on at my blog (since these blogs don’t support MathJax) →

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Amit Gupta

So You Still Don’t Understand Hindley-Milner? Part 2

Amit Gupta
Saturday, June 8, 2013

In Part 1, we said what the building blocks of the Hindley-Milner formalization would be, and in this post we’ll thoroughly define them, and actually formulate the formalization:

Formalizing the concept of an expression

We’ll give a recursive definition of what an expression is; in other words, we’ll state what the most basic kind of expression is, we’ll say how to create new, more complex expressions out of existing expressions, and we’ll say that only things made in this way are valid expressions.

Read on at my blog (since these blogs don’t support MathJax) →

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Amit Gupta

Entropy: How Password Strength Is Measured

Amit Gupta
Saturday, June 8, 2013

Mike Sierchio wrote a cool post on password strength, and the concept of entropy. As he points out, entropy isn’t always entropy. That confusion is apparently not uncommon, as it’s been asked about on IT Security Stack Exchange as well. So what’s really going on?

Let’s step back for a sec and fill in some context. What are we trying to do? We’d like some way to measure how hard it is to guess our passwords, a number that serves as a heuristic standard of password strength. But there are two fundamentally different things we might want to measure:

  1. How hard would it be for someone to guess your password with essentially no knowledge of how you created your password?

  2. How hard would it be for someone to guess your password if they knew the process used to generate it? This is of course assuming that there is a process, for example some script that does some Math.rand-ing and produces a password string.

The term “entropy” has been used to refer to both kinds of calculations, but they’re clearly entirely different things: the former essentially takes a string as input, the latter takes a random process as input. Hence, “entropy is not entropy.”

Alright, well if entropy isn’t entropy, let’s see what entropies are. We’ll look at the standard mathematical formulation of the random-process-entropy which comes from information theory. And we’ll look at the function used to calculate particular-string-entropy in one of the most popular password strength testers. And that’s all we’re going to do, we’ll look at how the calculations are done, without dwelling too much on the differences between the two approaches or what their use cases are.

Read on at my blog (since these blogs don’t support MathJax) →

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (783)
  • rails (117)
  • testing (90)
  • ruby (86)
  • ruby on rails (71)
  • jobs (62)
  • javascript (59)
  • techtalk (44)
  • ironblogger (42)
  • rspec (39)
  • bloggerdome (34)
  • productivity (34)
  • activerecord (30)
  • rubymine (30)
  • git (29)
  • gogaruco (29)
  • nyc (27)
  • design (24)
  • mobile (23)
  • pivotal tracker (22)
  • process (21)
  • cucumber (21)
  • jasmine (19)
  • ios (18)
  • tracker ecosystem (17)
  • webos (17)
  • objective-c (17)
  • fun (16)
  • android (16)
  • palm (16)
  • ci (16)
  • "soft" ware (16)
  • bdd (15)
  • tdd (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • css (14)
  • gem (13)
  • mouse-free development (12)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • api (12)
  • keyboard (11)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
Subscribe to Labs Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. ...
  10. 129
  11. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Tools
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >