Osper Technology Blog

Osper geeks explain how to be an awesome development team through joyful work, small teams, low bug counts, and more!

Dan North Visits Osper

A guest post from Dan North, originator of BDD and Accelerated Agile, who visited our office recently for a day to help us toward our goal of becoming awesome


Introduction

First impression was a friendly, welcoming office. Start-up vibe along with a sense of doing something socially meaningful. I met Carly at the door and she showed me up to the office. She’s only been there 5 minutes and was already happily settled in.

Squirrel gave me an overview of Osper’s purpose and its operating model. He also outlined the six goals he is targeting to make Osper awesome! Along the way he identified his particular headaches:

Technical 

  • Integration/API pain, since most heavy lifting is done by third parties
  • Wanting to instrument the process to learn more about where users fall off during the application process

Business 

  • Conversion rate could be improved.
  • How to prepare for scaling users
  • Developers as single point of failure - how to scale team size

We drew a big picture diagram of the various dependencies and partners. I introduced the team to CRC modelling so we could demonstrate the responsibilities and relationships between the various entities. I wasn’t very surprised to learn most of the integration still uses FTP!

We discussed a number of specific questions:

  • How can a small team operate at speed without getting in each others’ way and without creating legacy code and technical debt along the way? This is important in the context of the awesomeness goals.
  • How to be confident of security concerns as the organisation grows and as more developers get involved and are building and deploying the application.

The goal of the day was to think about how we could bring a new developer on board so they can get up to speed quickly, start producing high quality software that is consistent with the rest of the codebase, and do all this with the minimum impact on key developers. I took the role of the new developer, asking lots of questions, challenging what I was seeing and calling out the things that were confusing me.

The key to a number of the awesomeness goals is simplicity, and the biggest lever to achieving that is consistency. More about that later.

There is already a strong culture of automated testing, including:

  • Unit testing
  • Integration testing of each component
  • Acceptance and user story testing
  • Code review
  • Jenkins has a series of build promotions so a change in core, say, triggers a series of dependent builds.

We talked about reluctance to ship iteratively, for example an email that kept being delayed because “the copy wasn’t final”. This meant a significant amount of manual work. There’s a transition point between knowing all your customers by name (“Mary called about her card”) and having to introduce and iterate on automated processes in order to scale with only a handful of staff (another of the awesomeness goals).

One of the challenges is how to move the codebase as new ideas come into play. For instance the team have been introducing an event-sourcing model, which means some of the email code is the old style and some is new style. Often both are mixed within the same modules. This means it’s difficult for someone without suitable context or history to make a good decision.

On the subject of events, I mentioned that moving to an evented web server like Twisted or Tornado, which is already on the cards in terms of supporting scalability, would mean you could use the server as an event bus as well as a web server.

I also mentioned allocating some kind of unique identifier to any event as it hits the boundary of the system, and keeping that identifier with any other events the interaction generates, so you can correlate events across different services over time. We also discussed introducing Redis as a distributed store and atomic counter for events. It’s something the team was already considering.

Fixing a bug using tests

The afternoon was an introduction to doing work using a reported bug as a vehicle. I suggested I should drive and “act like the new guy”. The developer I was working with (my “pair”) showed me around the code, and we started working on a bug where an additional card wasn’t being activated. We bounced around the code a bit and my pair explained where he thought the bug might be. As he was describing it, the act of describing the flows caused him to rethink.

We decided to write a test, which was a good introduction to the test infrastructure and how we do test automation. We got into some discussion around test tools, in particular BDD tools like Cucumber, Lettuce, etc. and how much value they provide. We agreed it depends on the type of test, and where in the application you are testing.

The next section gets into some detail around testing, which is where we spent much of the afternoon. The tests we looked at are in a hierarchy of test classes and mixins, which means it isn’t obvious just from reading a test what is actually going on. Setup steps are running in classes defined elsewhere, relationships are being established between magic values and properties, and often these relationships are tacitly assumed in a particular test.

We started over with a new class, borrowing from the surrounding tests and creating small helper methods. I start with the simple structure of:

      def test_does_something(self):
        # given
        # we set up the context for the test
        # when
        # we execute the application
        # then
        # we can assert what should happen

We gradually replaced the comments with methods that we implemented elsewhere in the test case, and ended up with a nice, narrative-looking test that told a story. The test was about a parent ordering an additional card for a different child. Parents and children get validated, which cause cards to get activated.

The test ended up looking something like this:

      def test_validating_a_child_activates_additional_card(self):
        # given
        parent = new_parent(validated=True)
        child = new_child(parent, validated=False)
        additional_card = new_additional_card(parent, child)
        # then
        assert_card_is_inactive(additional_card)
        # when
        validate_child(child)

# then
assert_card_is_active(additional_card)

The narrative here is nice and clear. We pass a reference to the parent when we create the child, so they are obviously related. Likewise when we create the additional card it needs to know about the parent and the child.

We assert the status of the card before and after validating the child, so we know that’s what causes the card to become active. You can look at the implementation of the helper functions to see how children get associated with their parents, but it isn’t a necessary part of the test. On the other hand it doesn’t happen in a magic set-up method, it’s right there in the body of the test.

We noted that there were a number of cases for our test, and that only one of them had the bug we were investigating. Of course this was tacit knowledge and we wanted to get it explicitly into the test, so we used the DDT Python library to explicitly test the various combinations without writing lots of identical-looking tests. I hadn’t used DDT before and I really like it.

By the end of the afternoon we had a nicely-written test, we had exchanged lots of ideas around what constitutes good testing, the value of consistency and how to balance keeping things consistent with introducing new ideas (especially in the context of the event-sourcing design changes), distributed working and a number of other areas.

All-in-all, my day as a newbie felt very productive: we had a bug identified, test-driven, fixed, committed and building in Jenkins by the end of the day. Pairing worked well and I encouraged the developers to pair much more often and on more kinds of work. Pairing, along with the code reviews that are already underway, is a great way to create and reinforce consistency of values and vision.

Awesomeness goals

Osper has a number of explicitly-stated goals in its self-styled “pursuit of awesomeness”, inspired by various organisations they see as great places to work. At various times during the day we talked about how they might move towards some of these.

Live and work anywhere in the world

We chatted about how Github has a single jukebox so that everyone is listening to the same music. Also how spending a bit on tech can have a huge impact on people’s ability to work remotely. It’s a much more realistic proposition than it was even five years ago.

No more than 1 visible bug

This is about moving towards Continuous Delivery. This will be a challenge as Osper grows, particularly when you are working with financial or user-impacting features.

Millions of customers, 20 engineers

The current set-up is already on a good footing, using partners for heavy lifting and cloud services wherever it makes sense. The challenge will be in creating and maintaining consistency as the codebase and customer base grows. Having lots of engineers working independently in a large codebase, without clearly-articulated and well-understood technical and operational guidelines, will cause a reinforcing loop of increasing complexity requiring more people, and with more single points of failure.

Eliminate legacy code and technical debt

This is closely related to the previous goal, and I see it is a good indicator of whether you are on track. However it isn’t the main point. Consistency and simplicity will address both legacy code and technical debt, but not vice versa.

Idea to live product within a month

This will be a huge benefit and a big differentiator for any kind of financial services firm. Being able to genuinely field-test an idea within weeks is gold. Related to the Continuous Delivery goal, one challenge here will be around compliance and ensuring you have sufficient regulatory cover to be able to iterate quickly.

Joyful, energised environment

Keeping this as you start to grow requires commitment from everyone. Big doesn’t necessarily mean slow or formal or overbearing, although that seems to be the default if you don’t monitor it carefully. Keeping a deliberate organisational design and not being afraid to have an opinion is key to this. You want people to be self-organising, not self-directing. Otherwise chaos will ensue. 

blog comments powered by Disqus