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

Monthly Archives: October 2008

Pivotal Labs

Better array assertions using collect

Pivotal Labs
Monday, October 27, 2008

You could do this:

Person.tall_people.length.should == 3
Person.tall_people[0].should == people(:linda)
Person.tall_people[1].should == people(:dwane)
Person.tall_people[2].should == people(:rick)

This is better, because it’s clearer, and because in one stroke you prove bounds, content, and order:

Person.tall_people.should == [
  people(:linda),
  people(:dwane),
  people(:rick)
]

I argue that this is best:

Person.tall_people.collect{|p|p.first_name}.should == [
  "Linda",
  "Dwane",
  "Rick"
]

Failures messages are a pleasure to read

  expected: ["Linda", "Dwane", "Rick"],
  got: ["Juliette", "Jeanne"] (using ==)

And code is clearer overall (the price is a “collect”)

Collecting away from the original object (and into primitives) is not only clearer, in most cases your aim is not to re-prove that the element objects are fully and properly configured. You’ve already done that elsewhere. You only want to prove, in the simplest possible terms, that the group of things you got is what you expected to get.

Let’s say you don’t intend to care about order. Sorting on primitives is a snap:

Person.tall_people.collect{|p|p.first_name}.sort.should == [
  "Dwane",
  "Linda",
  "Rick"
]

Slightly more controversial:

Person.short_people.collect{|p|p.first_name}.should == []

The collect seems silly at first glance, but you’re making present and future assertion failures much friendlier. You’ll be happy about brain cycles saved and sanity kept during big refactorings.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

Standup 10/24/2008: Floating point number formatting and you

Adam Milligan
Friday, October 24, 2008

Interesting Things

We have a project that generates a lot of highly precise floating point numbers. However, we primarily want to display these numbers with only two decimal places of precision. In addition, we want to display these numbers with standard comma delimiters to the left of the decimal point.

Sadly, the Ruby #sprintf method provides the former functionality, but not the latter. What to do? Use a Rails helper, of course.

The NumberHelper from ActionView provides some useful functionality, so we used that. As it turns out, we found the best way to get the formatting we want to be using the #number_to_currency function with no denomination.

Also, rather than mixing the entire helper into the Float class for just one method, we chose to mix the helper into a nested class and expose only the functionality that interests us. The result looks something like this:

class Float
  class RailsNumberHelpers
    extend ActionView::Helpers::NumberHelper
  end

  def formatted
    s = Float::RailsNumberHelpers.number_to_currency(self, :unit => '', :precision => 2).chomp('0')
  end
end
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

Standup 10/23/2008: ARMailer warning!

Adam Milligan
Thursday, October 23, 2008

Interesting Things

  • ARMailer and ExceptionNotifer: A match not so much made in heaven.

If you use the ExceptionNotifier plugin (and if you don’t, why not?) and you install the ARMailer plugin, your app will stop sending the exception notifications. You have been warned. Initial reports suggest that fixing the problem is relatively straightforward.

For those not in the know, ARMailer is a plugin that queues all outbound email in your database, to be sent later by a cron job or something similar. Good for not clogging up your server with email processing during peak load.

Ask for Help

“Is it okay to load a Flash widget multiple times on a single page?”

General murmuring led to the conclusion that this works fine.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Testing modules that get included in a controller

Pivotal Labs
Wednesday, October 22, 2008

We recently ran into a problem of testing a module that was getting included in multiple controllers. More specifically, we wanted to test a before_filter that the module was adding to each of the controller.

The code uses rspec for testing and we found out that there was no easy way for us to test the module.

The simplest way that would have worked would have been to test the controller that included the module. This was our first cut even though we were not happy with it at all since it was not testing the module in isolation. After asking around, we finally figured it out.

Here’s some code

module ShowPageModule
  def self.included(base)
    base.class_eval do
      before_filter :show_title_in_view
    end
  end

  def show_title_in_view
    #do something..set some instance variable
    @fancy_page = true
  end
end

We want to test the show_title_in_view method.
The idea is to create a dummy controller and include the module so that you can test the method

So here’s how you do this in rspec
We define the controller, add some routing code and include it in test


class FakeController < ApplicationController
  include ShowPageModule
  def test
    head :ok
  end
end

ActionController::Routing::Routes.add_route('fake_page/test', :controller => 'fake_page', :action => 'test')

describe 'ShowPageModules', ' included in a ' do
  describe FakeController do
    it "declares a before filter that sets the variable" do
      get :test
      assigns(:fancy_page).should be_true
    end
  end
end

It is that simple and you now have a very isolated unit test.
The trick was to figure out the routing code.

Some caveats

  • Since routes are singleton, be very careful while adding routes
  • Also try to name your fake controller uniquely so that there are no conflicts
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Standup 10/20/2008: Preventing Flash from Reloading

Joe Moore
Monday, October 20, 2008

Interesting Things

  • We discovered why a Flash widget was reloading itself: changes the CSS position value. We were hiding the Flash widget by moving it’s containing div off the page with position: absolute; left: -9000, and removing the class that had those values to show it again. It turns out that changing that position value causes the Flash to reload. By keeping the position:absolute setting when we both show and hide our container div, the Flash no longer reloads.

Ask for Help

“When using Rails’s date_select helper, is there a corresponding helper method to turn that date format into a Date object in the controller?”

Some Snippets are available, but how about a Rails built-in solution?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Effective Markdown Editing with the WMD editor and the Save Text Area Firefox plugin

Pivotal Labs
Saturday, October 18, 2008

Anytime I need to edit Markdown, I reach for the WMD editor. Their splitscreen demo is the most effective way to edit Markdown that I have seen.

The left screen is the editor and the right screen is the “real-time” preview of the Markdown. It is nice because I don’t have to press a preview button to see rendered Markdown. The Markdown is also rendered as I type so I get instantaneous feedback of my changes.

There is also a Save Text Area Firefox addon, which enables me to save/load the contents of a text area to/from a file on the filesystem. Also the Ctrl+s shortcut saves the file.

So when editing Markdown, I:

  1. Open Firefox and go to http://wmd-editor.com/examples/splitscreen?blank=1
  2. Load or Save a markdown file by right-clicking the editor screen
    1. Going to the Text submenu
    2. Clicking Load or Save As
  3. Edit the file and see the generated output

Of course, its not a text editor replacement, since the possible text manipulation in Firefox is limited, but the feedback that is provided by WMD is very effective to rapid Markdown editing. I hope this sort of UI becomes more common.

Now if only there were a similar Textile editor…

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 10/17/2008: treetop

Pivotal Labs
Friday, October 17, 2008

Interesting Things

  • Treetop – Ruby-based DSL for text parsing
  • Some projects tried jRails and prefer to use jQuery directly instead jRails is jQuery for Rails
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Edward Hieatt

OpenView conference

Edward Hieatt
Thursday, October 16, 2008

OpenView Venture Partners

Ian McFarland and I attended a 2-day Development Forum hosted by OpenView Venture Partners last week in Boston. It’s the second year that Pivotal Labs has participated in the event. Open View has a portfolio of companies from all over (Europe, Australia, the US), each of which has been working on implementing Scrum over the past year. The engineering staff from 10 portfolio companies attended the event. Jeff Sutherland (amongst other things the co-creator of Scrum) is a Senior Adviser to OpenView; he provides advice and guidance to the portfolio companies as they progress through their Scrum adoption, and he gave a talk at the Forum. Pivotal Labs was invited to speak and lead a discussion of on 2 core topics: Developer Testing and the Principles of Build.

Status & Issues

First, we heard from each company on how they were doing with their adoption of Scrum and on the following two questions on the subject of technical developer practices:

  1. The goal of every sprint is to have fully tested quality software product ready to go live with customers. Where are you now with relation to this and what stands in your way of getting there?
  2. The goal of every development team should be to have one piece of software code globally, and all work and testing is done on this one piece of code, with multiple builds with built-in testing done in a given day. Where is your team in relation to this goal and what stands between you and getting there?

The 10 portfolio companies come from a disparate set of industries and technical domains, so Ian and I were very interested to hear each of their histories with Scrum and the current issues they were facing with respect to these 2 questions on developer practices. Some were at a fairly advanced stage – they had good test coverage and a stable CI setup – and some were just getting started. The most frequently stated barriers to achieving the goals of fully tested software that’s ready to release were:

  • A perception that the team wasn’t big enough to allow for writing tests
  • An assumption that ongoing developer testing could potentially slow the team down
  • An assumption that adding tests to a code base would mean first stopping and covering all the untested code so far with tests, and that therefore it meant stopping for a long period to retrofit tests
  • A question of whether clients even wanted frequent releases (e.g. once per week) of the software
  • A perception that the team wasn’t big enough to allow for pairing
  • A lingering assumption that QA and not Developers should be responsible for generating all test coverage

Pivotal Labs’ talks

Developer testing

Pivotal’s first talk was on developer testing. There were two main points we wanted to make:

  • The best way we know to get a big jump in quality and to be able to frequently release new versions of software is to rethink who is responsible for testing.

Rather than the traditional model of a QA team being almost solely responsible, consider a shift towards the whole team being responsible, and in particular a much greater emphasis on developers owning quality. For many developers it’s a radical shift.

  • The most effective way we’ve seen for developers to own quality is through the disciplined and sustained practice of TDD.

During the talk I gave a demo of a simple example of strict TDD, which gave rise to some useful conversations; as expected, the reactions varied from “yes, that’s what we do” to “that makes no sense!”. Having coded solely with strict TDD for almost 9 years now, and being around Pivots who exclusively test-drive also, it’s always interesting to hear reactions of people coming to TDD for the first time. The idea that tests are the center of the development effort, and that code is to some extent expendable, is a radical shift in thinking. We also touched on the benefits TDD brings in addition to reducing regressions. I find that a useful question to ask is what TDD stands for: “Test Driven Development” or “Test Driven Design”. The notion that TDD helps in designing your object model brought up some interesting discussions (for example, mock objects came up).

We also tried to address the barriers to adoption that had been brought up:

  • When introducing tests to a legacy code base, rather than trying to immediately cover everything with tests right away, we suggested:
  • “Stop the rot” – from now on, attempt to test-drive everything, including bug fixes
  • Spend time each iteration/sprint adding some coarse-grained, high-level tests around the legacy code
  • Once there’s at least a basic safety net in place, spend time refactoring towards unit-testability
  • Gradually introduce unit tests over time, with the goal being high test coverage at the unit level
  • Certainly with a small team there may not be bandwidth for an explicit traditional full-time QA role. We pointed out that with developer testing there’s no tester-developer separation: every developer is a tester, so testing isn’t gated by team size at all.

We made two points about speed when doing TDD:

  • It can certainly take some time to get proficient and achieve the same short-term speed you had before. However, in our experience, if a team has a member who’s done it before, or whose role it is to advocate for constant TDD, the team members start to get the hang of it much faster than you might expect – 3 weeks or less in many cases.
  • Independent of whether coding a certain feature is faster or slower, the medium- to long-term benefits (catching regressions instantly) are invaluable.
    There was an interesting discussion of why we’re so interested in pushing for frequent releasability even on projects whose clients are known not to want frequent releases. The consensus was that even if there’s no actual need or desire to release frequently, the act of trying to get to that point brings lots of benefits. There are fewer surprises when a real release comes due; the “last-mile” problem is often reduced; people integrate their changes with the main trunk of development more frequently, so there’s less merge hell, etc.

Build

Our second talk was on build. As an ideal to shoot for, we promoted what Pivotal Labs does:

  • Checkins prompt a build (clearly, this helps to identify which changes broke what)
  • A broken build is an anomaly – teams should immediately stop and fix a broken build
  • Keeping the build fast is critical, so that it stays relevant
  • The build must be easily visible to the whole team. At Pivotal we have 2 large TVs on the wall that clearly show the build status of all our projects

There was a good deal of discussion over how to get started with a build. Some ideas that were brought up were:

  • You don’t need tests to have a build. Compiling and packaging your code with each checkin, and making sure everything works is still very useful information
  • If your build is flaky (i.e. some tests fail seemingly at random), immediately detach the flaky tests to keep the build stable. Work on the flaky tests in isolation. The team needs to trust the build’s status.
  • If your build is slow, break out a slower suite of tests and run it in a separate process that runs less frequently. Work towards high coverage with fast unit tests so that the status of your fast build is meaningful.

Hopefully our talks were useful. Certainly they sparked plenty of discussion!

Thanks to Jeff Sutherland, Igor Altman and Steve Rabin of OpenView for inviting Pivotal Labs to speak.

Below are our two presentations.

Pivotal Labs Open View Presentation Quality Assurance And Developer Testing
View SlideShare presentation or Upload your own.

Pivotal Labs Open View Presentation Continuous Build
View SlideShare presentation or Upload your own.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

NYC Stand Up 10/15 – 10/16 2008

Pivotal Labs
Thursday, October 16, 2008

10/15/2008

Help!

  • Autocompleters – Which one do we use?

    • We are currently using the YUI autocompleter from YUI 2.4.1 which works well. However, we are making significant architectural changes, and the “new” way used the YUI autocomplenet from 2.6.0.
      We don’t want to install YUI just for the autocompleter and we don’t want to deal with the version mismatch, so we are going to use a different autocompleter. It seems that there are many autocompleters, so which one do we use?

    • NOTE: We ended up going with the jQuery autocompleter. The autocompleter had issues with noConflict mode and with pressing the backspace key. We fixed them and will post the fixed version to Github soon.

Interesting

  • Postgres replication requires a primary key, and HABTM solutions typically do not use primary keys. Has many through is better anyway.

10/16/2008

Help!

  • CI build is slow and we don’t know why

    • Our CI build is failing because of the performance issues. Some strange things we noticed:

    • There are many Sockets in the TIME_WAIT state

    • A possible solution is to run the OSX diskutils on the CI box.

    • Piston Hangs when importing a SVN dependency into a Git repo

Interesting

  • The Heavy team is coming for lunch to discuss process

  • git-svn is difficult to work with

    • Use all Git or all SVN if you can.
  • Polonium

    • Using click_and_wait fixes failures due to timeout on the CI box. It is more performant because click_and_wait is implemented in JS. The standard Polonium wait_for assertions poll into SeleniumRC.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Moving from Subversion to Git

Pivotal Labs
Thursday, October 16, 2008

Moving from Subversion to Git

We recently moved our project from subversion to git, and so far the move has gone very smoothly. The following post will detail what we did to make the move.

Our setup

For this project there 2 pairs working and we have 6 machines and one hosted service:

  • Two Mac OSX workstations with IDEA
  • One continuous integration server running Cruise Control
  • A staging server running a 2-year old version of Ubuntu
  • A subversion server
  • A production server hosted on Engine Yard
  • A Github account with the ability to create private repositories

The goal was to have one pair continue to work while the migration from svn to git was happening.

Getting the right software

Every machine must have git installed for this to work, and at least one workstation must have git svn installed. On Mac OSX we were able to install git reliably via macports with sudo port install git-core.

For the workstation that needed git svn it was a little more difficult. On a fresh install of OSX with macports, you should just be able to run sudo port install git-core +svn, but this requires perl bindings for svn and there are a number of dependencies that might be wrong if you’ve installed subversion from source or have conflicting macports.

On one machine, we were able to get past it by ininstalling and reinstalling subversion before running sudo port install git-core +svn

On a modern Ubuntu machine you should be able to run sudo apt-get install git-core, but since our Ubuntu machine is a few versions behind we had to install from source from http://git.or.cz/.

We planned on converting our svn externals to piston once we moved to git, so we also installed piston. We followed the instructions from http://technicalpickles.com/posts/piston-and-git-for-the-win to install it locally, since it’s not available as a standard gem.

Cloning the subversion repository

To clone the repository we used git svn. We only had a trunk on our project, so it was a bit easier for us – to start, all we had to do was:

git svn clone svn://our/repository/trunk

If you have branches and tags you may also want to add the --trunk, --branches and --tags flags as well – see the docs for more info.

For us the cloning took almost half an hour. When it was done we had a branch named git-svn (visible only with git branch -a), which we converted to master with:

git checkout -b master git-svn

We noticed when we looked at git log that we were missing several months from the commit history, even though the git svn command exited without error. To grab the rest of the revisions we ran git svn rebase and it finished the import. I’m not sure if this is a documented behavior or a bug, but it was surprising to us even though it was easily fixed.

Attributing checkins to pairs

We used to add the pair’s initials to the beginning of our svn commit comments, but with git there is a more elegant way to do it by changing the name of the pair.

In it’s simplest form, you can just change the git config user.name every time a new pair sits down. Brian Takita suggested listing all the pairs in the .git/config file, and just uncommenting the right one in the morning, which saves some time.

There are much more complex solutions, like [this][http://www.brynary.com/2008/9/1/setting-the-git-commit-author-to-pair-programmers-names] if you like as well (well worth the read even if you don’t end up using it)

Re-ignoring files

Any files that were ignored in svn are no longer ignored in git. We added several standard files to our ignore list, like the

  • log/*
  • IDEA project files
  • .DS_Store

Ignoring the log entries seemed to remove the log directory entirely, which caused us to have to manually create the log file on our different servers, but seemed like the right thing to do.

Grabbing externals

Our git svn clone did not pull in any of our externals. We added these back using piston, although it only worked for a few of the externals. The latest version of piston has support for adding svn repositories to git projects. The syntax is the same as it normally is:

cd your-project
piston import svn://some/svn/repo vendor/plugins/some_plugin

For us this worked about half the time, and the other half it hung endlessly and never finished the svn checkout. We still don’t know why that happened. For the plugins that we couldn’t piston we just svn exported those to our project, and once we figure out how to fix piston we’ll piston those as well.

Brian Takita mentioned that when there is a big pending changelist, piston becomes slower on git. In our case, we tried when there were no pending changes, and it still hung.

Adding git support to IDEA

Since we use IDEA for development, we decided to install a git plugin for IDEA to make it easier to view. We decided on git4idea because the project has been more active on github recently and seemed to have a decent feature set.

git clone git://github.com/markscott/git4idea.git
cd git4idea
cp Git4Idea.jar /Library/Applications/<your IDEA directory>/plugins

Then restart idea to make sure it takes effect. We added the .git directory to the list of ignored modules so that it wouldn’t appear in search history as well. A few things to note with this plugin:

  • when you commit, it does not push – the git workflow typically involves committing locally and then pushing in two separate steps
  • when you push it will ask you for a target – the default is default/origin or something like that – in our case typing “origin” works
  • the project explorer seems to get out of date easily. We run Version Control > Refresh File status often whenever it looks funky (or type “Alt + C, E” if you’ve got the alt keys working)
  • when you commit, there are annoying comments in the commit message textarea – it’s focused by default, so you can easily delete it, but it’s still annoying

We haven’t used the plugin extensively, but so far it looks decent.

When switching IDEA projects, you might find it helpful to copy your old project files into the new project to save some time re-setting your settings.

Pushing to github

Once you are setup, you can push to github. In our case we did the following:

  • added our local machine’s public ssh key to the account’s ssh keys (on githubs main account page)
  • created a new repository and marked it as private
  • followed the github instructions to add the remote repo and push to origin master

We decided to only push the master branch to github, since we wouldn’t be using git svn for more than a few hours as we migrated all of the machines. We didn’t test the continued use of git svn after cloning the repo, so if you need to continue with svn you may need to push the original branch as well, or change your .git/config file to make sure that dcommits still work etc…

Once we had it on github, we renamed our original project directory and did a fresh git clone of the github project, then:

  • updated our .git/config again with our pair names (alternately, you can set the config variables globally, like git config --global user.name 'foo & bar')
  • added the log directory (mkdir log)

Updating CI

To update the CI box, we:

  • Added a deploy key to github
  • Stopped all running cruise processes
  • Renamed our current project to -SVN
  • Updated to the latest version of CCRB
  • Added our new project with the standard cruise command
  • Updated our “scratch pad” checkout from the svn repo to the git repo
  • Rebooted CI to make sure everything worked (you probably don’t need to reboot)

Github provides the ability to create read-only accounts for specific repositories. These users are identified by public keys and they must be unique across github. For us, we had to:

  • Log into our CI box
  • Create a public/private key pair with ssh-keygen
  • Copy the public key to github’s deploy key area in the Project Name > Admin section

Then we updated to the latest version of Cruise Control by going to CCRB directory and running git pull. If you’ve added Cruise Control by some other method, you’ll have to update to the latest source from http://github.com/thoughtworks/cruisecontrol.rb/tree/master.

In our setup, our projects are in ~/.cruise/projects. Cruise Control loops through every directory in /projects and loads it’s cruise_config.rb file, so you can have multiple builds running at the same time. After renaming our original project, we added the new project by going to ~/.cruise and typing:

~/.cruise./cruise add your-projectname --repository git@github.com:projectname.git --source-control git

This checked out the repository and added the necessary config file. We then went in and manually created the log directory.

On our CI box we also have a scratch-pad checkout of our code, useful for debugging, located at ~/workspace/project-name. We blew that away and git cloned our repo, then added the log directory.

Once the CI build is green, you can delete the old project that was based on svn.

Git on capistrano

To deploy our app to our staging server, we:

  • Created a deploy key for the staging server and added it to github
  • Deleted all files from the remote cache directory
  • Updated deploy.rb to use git
  • Deployed twice (the second time to ensure that it worked from the remote cache)

We followed the excellent guide here to get our deploy settings correct.

The only stumbling block we found was that we use deploy_via :remote_cache, which stores a checkout of the repository on the server to make deployments faster. Since the remote cache had a subversion checkout, it was necessary to delete all files in the cache before deploying.

After the cache was cleared and deploy.rb was updated we deployed twice, both times without incident.

Deploying git on Engine Yard

We haven’t had the opportunity to deploy on git to EY, but given how easy it was to deploy on our staging server we anticipate that EY will be easy to deploy.

Josh Susser pointed out that because Github itself if hosted on Engine Yard, git deploys from git repos on Github to slices on Engine Yard servers are blazingly fast.

Managing the transition

While one pair was updating these servers, the other pair was checking into the existing subversion repository. Once all the work stations were set up, CI was up and deployments were working, we ran a final svn rebase and pushed to github. It looked something like this:

cd svn-project-dir
svn commit -m "made some well tested changes"`
cd git-project-dir
git pull        # => gets all of the changes that the other pair made, i.e. pistoned directories etc...
git svn rebase  # => gets latest changes from svn
git push        # => sends changes to github

So by the end of the day, there was almost no interruption for one pair.

Timing

All in all it took about a day for a single pair, but if we could do it again knowing what we know now we could probably do it in about half that time.

References

  • http://cruisecontrol.sourceforge.net/
  • http://git.or.cz/
  • http://technicalpickles.com/posts/piston-and-git-for-the-win
  • http://www.kernel.org/pub/software/scm/git/docs/git-svn.html
  • http://www.github.com/
  • http://github.com/markscott/git4idea/tree/master
  • http://github.com/guides/deploying-with-capistrano
  • http://groups.google.com/group/github/browse_frm/thread/c5d2620c541e4e1a
  • http://arthurkoziel.com/2008/05/02/git-configuration/
  • 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 Community Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. →
  • 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 >