Larry Price

And The Endless Cup Of Coffee

Deauthorizing Token With the Trello Client

| Comments

In my application, a user can connect to Trello without logging in. Whenever this “anonymous” user hits the landing page, I attempt to force the Trello client to authorize the user again. By doing this, the user can return to the landing page whenever he or she likes to switch usernames. My authorize code looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
function AuthenticateTrelloAlways() {
  Trello.authorize({
    name: "Ollert",
    type: "popup",
    interactive: true,
    expiration: "1hour",
    persist: false,
    success: onAuthorizeSuccessful,
    scope: {
      read: true
    },
  });
}

This works oh-so-wonderfully in Chrome and Firefox, but, even during the hackathon which spawned Ollert, we noticed that IE10/11 were causing some unexpected issues. Authorization would work the first time the user hit the landing page, but on subsequent visits telling Trello to Allow or Deny access resulted in the popup showing a white screen and never calling my callback function. Closing and reopening IE would allow me to authorize once, presumably until the “1hour” that I requested the original token for expired. I also verified this problem existed in IE9.

After several hours tweeting obscenities about IE, I stumbled upon the answer while browsing the source code for Trello’s client.coffee. About one third of the way through the code, I found this function:

1
2
3
4
5
# Clear any existing authorization
deauthorize: ->
  token = null
  writeStorage("token", token)
  return

All this code does is unset the class variable token and unset the local store variable of the same name. So I changed my AuthenticateTrelloAlways() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function AuthenticateTrelloAlways() {
  Trello.deauthorize();

  Trello.authorize({
    name: "Ollert",
    type: "popup",
    interactive: true,
    expiration: "1hour",
    persist: false,
    success: onAuthorizeSuccessful,
    scope: {
      read: true
    },
  });
}

VoilĂ . Why does this only happen in IE? I was originally going to blame the local store, but, since I was able to reproduce the defect in IE9 (no HTML5), I no longer believe that to be the case. I’m currently resigned to chalk it up as IE just being IE.

Lessons Learned During the Conception of Ollert

| Comments

During SEP’s previous startup weekend, I pitched an idea for a Trello Analysis Tool called Ollert. In less than three days, a team of 6 built a minimal viable product (MVP) and put it live on the internet. In a little over three months, I have slowly guided Ollert through the legal department, obtained a real domain with security, and fixed a few bugs along the way. I’ve learned a thing or two about seeing a hackathon project to fruition that I’d like to get in writing.

1. You Can Never Be Too Prepared

Before Startup Weekend, I spent days refining my idea and coming up with a cute little proof of concept. I even laid out work items for my developers to work on. When I finally discovered a clever name, I thought my preparation was over.

I was wrong.

Although I requested 5 developers to help me build Ollert, my plans only allowed for two developers to work simultaneously. I didn’t realize how hard it would be to keep everyone busy all the time, especially my less-experienced engineers.

Although I did proof-of-concept my idea before we started, I failed to proof-of-concept the more dynamic capabilities a multi-user web application needs to provide. This mistake cost another developer and I the first night of the weekend, plus a bit of the next morning.

Don’t even get me started about environment setup. Although I run Ubuntu natively and had all my developer tools ready before we started work Friday night, the rest of my team were users of That Other Operating System. I should have created a virtual machine with everything all set up, burned it to several USB drives, and let my developers set up VirtualBox without my intervention.

2. Talk to Legal

Why do I care about legal? Isn’t this my responsibility anyway?

As it turns out, putting a startup weekend project live on the internet before the weekend is over is really cool from a team perspective, but terrifying from a legal perspective. With our MVP, I wasn’t using SSL to encrypt user data. I didn’t consider how Fog Creek would react once they realized our name is their name backwards. I even had the chutzpah to stick my company’s logo in my site’s footer.

A quick discussion with management will prepare both sides for the “grand reveal” if the project makes it to launch. Personally, I’d like to see some level of legal counsel in the Startup Weekend “pre-pitch” session to get feedback before implementation.

3. Listen

The second day, one of my developers mentioned using endpoints in the application, and I rejected this idea on the basis that it was too complicated. On the dawn of the final day, we realized that we needed to use his approach or the application would be unusable. This required us to do a lot of rework that could have been avoided. I often think about what other ideas might have been suggested by my team that I may have accidentally ignored.

4. Easy Tasks

My assumption going into Startup Weekend was that my team would all be familiar enough with the technology to be able to “jump right in” or follow along with someone who could. This was a bad assumption.

It’s easy to find tasks that I can do. It’s much more difficult to find tasks that anyone could do. What are the less-involved tasks on your current project that a less-experienced developer could work on until they’re ready to tackle something bigger? Can they set up the database, or the tests? Can you find a guide for them to follow to do these things? If not, you’ll have people on your team who don’t feel involved but desperately want to help.

5. Start Small

Ollert is a pretty big idea, especially for a three-day project. I had high hopes of making it even bigger until the dawn of the final day. I wanted to include some sort of payment system to prove that we would be able to charge people when they sign up without actually charging them. This “feature” of fake payment has no place in a real product, and would have been misleading at best. We had dozens of ideas for statistics and charts to make it into the website that just weren’t that useful.

Limiting scope might have allowed us to come up with a more polished MVP; I walked away the final evening wishing I had left out sign up/in in favor of giving the application more sex appeal.

Conclusion

Starting a project is hard. It’s even more difficult when you have to delegate most of the work to other people. Startup Weekend is a manger and a mortuary, seeing the birth of many ideas and the death of most. I hope to take this experience and make an even better new product next Startup Weekend.

Jasmine - a Whole New World of Javascript Testing

| Comments

Jasmine: a headless Javascript testing library written entirely in Javascript. With similarities to rspec, I’ve quickly grown attached to this framework and have been looking for opportunities to discuss it. Version 2.0 was recently released, so I’ll be focusing on the standalone 2.0 concepts. To get started, download and uncompress the standalone distribution.

The uncompressed directory structure will have three subdirectories: spec, src, and lib. lib contains all the Jasmine source code. src contains some sample Javascript class that is tested by test files contained in spec. Outside of the subdirectories is the special file SpecRunner.html. This file is how we will run our tests.

Let’s start a new pizza place.

We’ll need Pizza. A Pizza will need several things: size, style, toppings, and price. We’ll have a few styles available, but also allow our guests to request additional toppings. We’ll also set the price based on the size and number of toppings. Create the files src/pizza.js and spec/PizzaSpec.js and add them to SpecRunner.html.

We’ll start by being able to get the styles from Pizza.

spec/PizzaSpec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
describe("Pizza", function() {
  var pizza;

  beforeEach(function() {
    pizza = new Pizza();
  });

  it("should give a choice of styles", function() {
    expect(pizza.getStyles()).toContain("meat lovers");
    expect(pizza.getStyles()).toContain("veg head");
    expect(pizza.getStyles()).toContain("supreme");
  });
});

The syntax is just lovely: We use describe to set visual context, beforeEach to perform a task before each spec, and it to encapsulate a test. The results of running SpecRunner.html in my browser:

spec/PizzaSpec.js
1
2
Pizza should give a choice of styles
  TypeError: pizza.getStyles is not a function in file:///home/lrp/docs/jasmine/spec/PizzaSpec.js (line 9)

Fixing it:

src/pizza.js
1
2
3
4
5
function Pizza() {
  this.getStyles = function() {
    return ["meat lovers", "veg head", "supreme"];
  }
}

And the results:

src/pizza.js
1
2
Pizza
    should give a choice of styles

Let’s set the toppings:

spec/PizzaSpec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
describe("Pizza", function() {
  // ...

  describe("toppings", function() {
    it("should have no toppings when no style and no extras given", function() {
      pizza.initialize();
      expect(pizza.getToppings().length).toBe(0);
    });

    it("should have only extras when no style and extras given", function() {
      var extras = ["pineapple", "edamame", "cheeseburger"]
      pizza.initialize(null, null, extras);

      expect(pizza.getToppings().length).toBe(extras.length);
      for (var i = 0; i < extras.length; i++) {
        expect(pizza.getToppings()).toContain(extras[i]);
      }
    });

    it("should have special toppings when given style and extras", function() {
      var extras = ["pineapple", "edamame", "cheeseburger"];
      pizza.initialize(null, "veg head", extras);

      expect(pizza.getToppings().length).toBe(7);
    });

    it("should have special toppings when given style", function() {
      var extras = ["pineapple", "edamame", "cheeseburger"];
      pizza.initialize(null, "veg head");

      expect(pizza.getToppings().length).toBe(4);
    });
  });
});

For these tests, I nested a describe block to give better context to what I’m testing. Fixing the tests:

src/pizza.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Pizza() {
  // ...

  var size, toppings;

  function findToppings(style, extras) {
    toppings = extras ? extras : [];

    switch (style) {
      case ("meat lovers"):
        toppings.push("ham", "pepperoni", "bacon", "sausage");
        break;
      case ("veg head"):
        toppings.push("onion", "tomato", "pepper", "olive");
        break;
      case ("supreme"):
        toppings.push("pepperoni", "onion", "sausage", "olive");
        break;
    }
  }

  this.getToppings = function() {
    return toppings;
  };

  this.initialize = function(pizzaSize, style, extras) {
    size = pizzaSize;
    findToppings(style, extras);
  };
}

And finally, I’ll deal with the cost. I’ll come out of scope of the nested describe and nest another describe.

spec/PizzaSpec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
describe("Pizza", function() {
  // ...

  describe("cost", function() {
    it("is determined by size and number of toppings", function() {
      pizza.initialize(10, "supreme");
      expect(pizza.getToppings().length).toBe(4);
      expect(pizza.getCost()).toBe(7.00);
    });

    it("is determined by size and number of toppings including extras", function() {
      pizza.initialize(18, "meat lovers", ["gyros", "panchetta"]);
      expect(pizza.getToppings().length).toBe(6);
      expect(pizza.getCost()).toBe(12.00);
    });
  });
});

To fix this test, I’ll use my handy-dandy pizza-cost formula:

src/pizza.js
1
2
3
4
5
6
7
8
9
function Pizza() {
 // ...

  this.getCost = function() {
    return size/2 + toppings.length * .5;
  }

  // ...
}

This is great and all, but a bit simple. What if we wanted to make an ajax call? Fortunately, I can fit that into this example using Online Pizza, the pizza API. Unfortuantely, the API is kind of garbage, but that doesn’t make this example any more meaningless. You can download jasmine-ajax on Github, and stick it in your spec/ directory and add it to SpecRunner.html. At this point I need to include jquery as well.

In order to intercept ajax calls, I’ll install the ajax mocker in the beforeEach and uninstall it in an afterEach. Then I write my test, which verifies that the ajax call occurred and returns a response.

spec/PizzaSpec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
beforeEach(function() {
  jasmine.Ajax.install();

  pizza = new Pizza();
});

afterEach(function() {
  jasmine.Ajax.uninstall();
});

describe("sendOrder", function() {
  it("returns false for bad pizza", function() {
    pizza.sendOrder();

    expect(jasmine.Ajax.requests.mostRecent().url).toBe("http://onlinepizza.se/api/rest?order.send");

    jasmine.Ajax.requests.mostRecent().response({
      status: "500",
      contentType: "text/plain",
      responseText: "Invalid pizza"
    });

    expect(pizza.orderSent()).toBe(false);
  });

  it("returns true for good pizza", function() {
    pizza.sendOrder();

    expect(jasmine.Ajax.requests.mostRecent().url).toBe("http://onlinepizza.se/api/rest?order.send");

    jasmine.Ajax.requests.mostRecent().response({
      status: "200",
      contentType: "text/plain",
      responseText: "OK"
    });

    expect(pizza.orderSent()).toBe(true);
  });
});

To get this to work, I add some logic to the Pizza class to set some state based on what the ajax call returns.

src/pizza.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var orderSuccess;

this.sendOrder = function() {
  orderSuccess = null;

  $.ajax({
    type: "POST",
    url: "http://onlinepizza.se/api/rest?order.send",
    success: function() {
      orderSuccess = true;
    },
    error: function() {
      orderSuccess = false;
    }
  });
}

this.orderSent = function() {
  return orderSuccess;
}

Ajax calls tested. By installing Jasmine’s ajax mock, all of the ajax calls were intercepted and were not sent to the server at Online Pizza. Any ajax calls that may have been fired by the Pizza class but were not addressed in the spec are ignored. The final test results look something like this:

src/pizza.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Pizza
    sendOrder
        returns false for bad pizza
        returns true for good pizza
    styles
        should give a choice of styles
    toppings
        should have no toppings when no style and no extras given
        should have only extras when no style and extras given
        should have special toppings when given style and extras
        should have special toppings when given style
    cost
        is determined by size and number of toppings
        is determined by size and number of toppings including extras

Full sample code available on Github. There’s a lot of other interesting things Jasmine can do that I’m still learning about. If applicable, I’ll try to create a blog post for advanced Jasmine usage in the future.

Git Out the Way - Rebase Workflow

| Comments

We use git on my current project, and we used to always use remote branches. After doubling the number of developers touching the repo, we found that remote feature branches led to merge conflicts, stale branches, and hidden code. We’ve switched away from using remote feature branches, favoring instead to commit directly to origin/master, making remote feature branches the exception. We do this using a simple method called rebase.

What does it mean to rebase?

When you rebase your local changes onto another branch, your changes become the head of that branch. For instance, say I start working off remote branch master, specifically changeset M2. I then make some changes and commit locally, which I’ll note as changeset L. While I was making my changes, some other developer pushed to remote branch master, resulting in changeset M3.

1
2
3
M1 -> M2 -> M3
        \
         ->L

This is where rebase comes in. I want to rebase my local commit on top of M3, which will make it the new head of master.

1
$ git rebase origin/master
1
M1 -> M2 -> M3 -> L

Now I can keep working locally. Until I push my local master to origin, every time I rebase will cause all of my commits to rebase on the head of master.

I like to keep as close to origin/master as possible since we have a fairly active repository. If I want to make sure I have the bleeding edge of master, sometimes I don’t have my local changes in working order when I want to rebase. Some people like to stash and unstash local changes, but that takes too much effort for me. I prefer just to use git commit --amend to constantly update the last changes I committed. You can only amend to an unpushed local commit, otherwise you’d be changing history remotely (which is bad). Using the amend flag allows me to keep one solid local changeset around until I’m comfortable with pushing it to origin. Also note that using amend also allows you to change the commit message of the last commit.

What happens when you need to use a remote branch?

Git’s best-kept secret is merge --squash, a wonderful flag that gives you all the benefits of a rebase (linear history, fewer changesets), without all the hassle of a big fat rebase merge (rebasing infrequently can lead to merges that will haunt you in your sleep).

1
2
3
4
5
6
$ git branch
* local-branch
master
$ git merge master
$ git checkout master
$ git merge local-branch --squash

This will give you a single changeset on the head of master containing all the changes you made in local-branch, including resolution of merge conflicts. merge --squash has eased a lot of the headaches my team had when stuck doing rebase merges previously.

Takeaways From AgileIndy 2014

| Comments

The AgileIndy Conference 2014 has come and gone. I wasn’t sure what to expect, since it was my first conference, but I came out of it feeling rather positive.

A short list of my favorite things from this year’s AgileIndy:

  • Interesting speakers
  • Good food
  • Uncomfortable chairs
  • #agileindy14
  • Free booze
  • Lots of coffee
  • Champion dogs

Good times. Personally, I found the first half of the day to be superior to the afternoon. I’m not sure if it was the speakers or if I’m just a morning person, but I’m much more interested in discussing what the morning speakers had to say.

The Dude

We’ve got hard problems to solve, we don’t need aphorisms, we don’t need truisms. – David Hussman

The event started out with the self-proclaimed “Dude,” David Hussman of DevJam. I’ve previously had some training involving Mr Hussman at SEP, during which we had trouble explaining our relationship with Agile: gargantuan project, lots of devs, massive codebase, hour-long builds, and an immature implementation of the so-called Scalable Agile Framework. I thought I knew what to expect during this talk, but I was delightfully mistaken. Everything Hussman said during his keynote fit with the conversations I’ve been having with my current teammates.

Hussman talked about “process mass,” or the weight that you carry as you gain more and more process on a project. “Measure your success by evidence, not by adherence” to the process. What good is completing a story if you don’t have anything to show for it, or if you don’t have the quality to back it up? Hussman discussed scrum, and the ridiculous concept of “scrum of scrums,” and potentially even a “scrum of scrum of scrums.” In his own words, “At some point you have to have something other than a naming convention.” Getting stuff done and feeling satisfied with your work is much more important than being able to audit the lifetime of a story, or what day of the week your team gets the most done, or how many meetings your ScrumMaster goes to.

Speaking of ScrumMasters (and Product Owners and Tech Leads for that matter), the Dude wants us to think critically about these roles. Why are Scrum Masters and Tech Leads in meetings all day? A Tech Lead mosies into standup in the morning just to say, “Yesterday I was in meetings all day, today I’m in meetings all day, no blockers.” “Somehow [we’ve] bonded the process to the people,” using process to hold team members hostage. We’ve found ourselves turning Agile into a bloated mess, and we’re in need of people to relieve the pressure.

Estimation

During the first break-out session, Steve Vance talked about estimation, one of my favorite topics to complain about.

We started out by defining terms. Unfortunately, we use the word “estimation” to mean almost anything, from the very precise to the very vague. We “estimate” in T-shirt sizes, story points, days, and hours. We all mean something different when we make an estimation; it all boils down to an opinion. “The work is going to take what it takes,” and everyone has an opinion on how long that is.

I mentioned hours in the above paragraph as a unit of estimation. This is a particular pet peeve of mine: I absolutely despise estimating in hours. What do we even mean when we estimate in hours? Vance says, “When I say hours as in this is going to take 3 hours, that does not mean 3 clock hours.” So what does an hour mean? My current team uses a tool called TFS, which actually forces you to answer this question. So we “estimate” that Danny Developer will be able to accomplish 5 hours of work every day. We just turned Danny’s day into hours on a clock. Now we “estimate” that a task will take “about a day,” which translates into 8 hours. Note that 1 task day != 1 Danny Day. We’re estimating tasks in these very precise units (real-life hours), and poor Danny will probably just work a 10-hour day to finish the task in “1 day,” which is what the team estimated the task would take.

T-shirt sizing or story points should be able to alleviate these problems, but we have to be vigilant not to concretely define these terms. Estimates should always be relative to yesterday’s weather and not based on real time frames. Once you’ve defined a 5-point story as taking exactly 5 days, you’ve destroyed the whole system and you might as well start estimating in hours again.

Anti-fragility

Si Alhir discussed the principles of “anti-fragility” during the pre-lunch session. Anti-fragility is a post-Agile concept. That’s right: post-Agile. I know you were just getting used to Agile, but it seems that we’ve created a monster that’s getting harder and harder to control. Teams find themselves saying “They’re not Agile, we’re Agile” when describing the pitfalls of other teams.

So what is anti-fragility? It’s easier to define in relative terms. Traditional teams are hurt by change and actively resist change; Agile teams embrace change and are able to inspect and adapt to it; Anti-Fragile teams embrace disorder and are able to adapt and evolve to chaos. “Fragile teams want tranquility,” while anti-fragile teams “gain from stress.” Anti-fragility involves shifting from focusing on avoiding risk to focusing on overcoming risk.

“What have you removed?” Si asks, reiterating Hussman from earlier in the day. “More rules, more process causes more fragility.” As our Agile teams add process, this becomes more evident. “Dependency,” “the line between dev and test”, and “centralized power” all cause fragility. Sprint cycles every 1-2 weeks cause a constant stress, which “will kill you, numb you.” Anti-fragile teams prefer “acute stress with recovery time” caused by overcoming risk as it materializes. The so-called ScrumMaster of an Agile team is protecting the team from risk, contributing to the overall fragility of the team.

“The most fragile thing in the Agile world is teams,” Si insists. “Consistent, co-located teams.” Teams should be able to be disbanded and created at-will or “just in time” as the need arises. Teams are created to attack “focal points,” or areas that need attention right away. This is an especially hard pill to swallow, since many of us like the familial feeling that comes with long-term teams. Not to mention the nightmares of billing if you’re a consulting company.

Conclusion

There were a few other speakers at the conference, but none hit quite as close to home as the three I’ve discussed. Overall a very interesting conference and a very fun experience. I’m excited to think about these ideas and I’d like to attempt to use some of them to make my teams more efficient and maybe even a little happier. It looks like the general flow is to move away from massive amounts of process, working more like Lean Startup or Anti-fragile teams.

A decade from now, we’ll be living in a post-anti-fragile world wondering what on earth we were thinking in this world of rules, definitions, estimations, and process.