Ruby Enumeration Library

I’ve released a little library as open source on github:

http://github.com/karlin/enumeration/tree/master

Enumeration is a small library that makes a specific type of enumeration construct easy to make and use. See the README in the repo above for more information.

Atomic Object in Rapid Growth

Rapid Growth TV created a video about Carl Erickson and Atomic Object. Check it out on the Rapid Growth Media site.

http://www.rapidgrowthmedia.com/features/rgtvatomicobject.aspx

Testing Swing applications using JRuby

Demetrius Nunes recently posted about his Swinger library to the JRuby mailing list. Swinger uses Cucumber and Jemmy to provide a test harness for automating Swing tests.

I responded to Demetrius letting him know how great it is to hear others are interested in automating Swing testing. We’ve been using a combination of FEST and Ruby helpers specific to our own Swing applications. As Demetrius notes, Swing tests can be frustratingly brittle, but they are still well worth the investment. Swinger looks like it is both extensible and can help alleviate some Swing testing pain.

You can check out my response here and the entire discussion here.

Table layout sucks... but I do miss valign

With margin: 0 auto; it’s pretty easy to center content horizontally, but it’s a pain without tables to center content vertically inside either a container or the window. Here’s a pretty simple CSS method I found. In this example, we’ll be centering in the browser window, but this can be applied inside a container as well.

Read the rest of this entry
Filed in: Web Magic

Omitting Needless Features

Recent bouts with our ailing Mephisto instance left me wondering where we could go next…

James Britt’s post over on Ruby Best Practices describes a scenario of writing software that I can really appreciate. We need a blog. Web app, right? Instead, start with something less, like a Sinatra app fronting a git repo. Now take out more stuff. Actually, just use static files and throw out the web app. Add Disqus to handle comments. Done. The resulting blogging engine is behind what Ruby Inside calls the the “best new Ruby blog this year.”

If you have time you could follow up by reading the well-aged piece Omit Needless Code by Kevlin Henney.

Nesting RSpec Describes

Like many people at AO, I have come to enjoy testing code in Jay Fields' one assertion per test style.

This style is great for testing because it clearly establishes intent for each test. This point is invaluable when another developer either inherits your code, or you revisit it at some point in the future. The main problem with this style is the need to create stubs for all the calls in the describe's before block, or resort to using stub_everything for all your mock objects. These are both fine solutions for simple code, but as soon as the logic encounters a condition you may be setting yourself up for potential testing bugs, and difficulty when you add new logic.

One way to avoid this problem is to leverage the power of rspec, and use nested describe blocks for each condition statement. This helps limit the before block for each describe to only define the stubs for that condition block. This is helpful because it prevents the tester from accidentally defining a stubbed expectation for something that should not happen. It also creates a nice placeholder for adding logic into the code in the future.

The following example shows how nesting rspec describes works. It is important to note that the branch that contains the most logic should be encapsulated in the sub-describe.

Example Source Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Example 
  def run(director, runner)
    runner.prepare
    if director.go?
      runner.run
      if runner.complete?
        director.stop
      end
      runner.stop
    end
    runner.finish
  end
end

Example Test Code with nesting describes:

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
describe Example do
  before do
    @target = Example.new
  end
  
  describe '#run' do
    before do
      @director = mock
      @runner = mock
      
      @runner.stubs(:prepare)
      @director.stubs(:go?).returns false
      @runner.stubs(:finish)
    end
    
    it "should prepare the runner" do
      @runner.expects(:prepare)
      @target.run @director, @runner
    end
    
    it "should check the director" do
      @director.expects(:go?).returns false
      @target.run @director, @runner
    end
    
    describe 'director returns true for go' do
      before do
        @director.stubs(:go?).returns true # redefine
        @runner.stubs(:run) 
        @runner.stubs(:complete?).returns false
        @runner.stubs(:stop)
      end
      
      it "should run the runner" do
        @runner.expects(:run)
        @target.run @director, @runner
      end
      
      it "should check if the runner is complete" do
        @runner.expects(:complete?).returns false
        @target.run @director, @runner
      end
      
      describe 'runner returns true for complete' do
        before do
          @runner.stubs(:complete?).returns true # redefine
        end
        
        it "should stop the director" do
          @director.expects(:stop)
          @target.run @director, @runner
        end
      end
      
      it "should stop the runner" do
        @runner.expects(:stop)
        @target.run @director, @runner
      end
    end
    
    it "should finish the runner" do
      @runner.expects(:finish)
      @target.run @director, @runner
    end
    
  end
end

I realize the test code gets a bit long, but it is very easy to understand and extend. Also, nesting on conditional breaks creates an easy framework to follow when writing test code.

Continuous Integration for Domain Names and SSL Certificates

Creating a perfectly functional web application isn't too helpful if you forget to renew the domain name, and DNS requests start going to a placeholder for-sale page. It doesn't matter to your customers if they were the ones responsible for managing registrar information. All they see is a broken website.

Any time you're working on a web project for a customer, you have many more things to track besides the software itself. You have accounts for web hosting, accounts for DNS, domain registration, SSL Certificates to keep renewed. All these things will have their own expiration dates, and they all have the potential to cause your customer's project to fail.

What we needed was a system that would track a variety of details about a project, specifically focusing on expiration dates.

In addition to just warning on expiration dates, the system does a few additional things:

  • Automatically grabs domain expiration from whois records
  • Automatically grabs SSL certificate expiration by connecting to the site and checking the actual certificate used.
  • Checks that the SSL certificate is valid
  • Checks that it can connect to a URL for the domain name and get a valid response.
  • Iterates through all the nameservers listed in the whois record, and checks that those servers return an A record for the domain.

A cron job is run hourly to update all the expiration dates and server statuses. The results are displayed in our continuous integration server, so everyone is aware of the results.

The system is implemented using Rails, although Rails is really only being used as a means to provide an easy configuration editor and an object model to represent the data. Streamlined is being used to provide an interface for adding and deleting resources. Streamlined is an advanced scaffolding plugin for Rails.

Here are some screenshots and samples of output from the system:

This is the configuration screen for the application, to define the resource that should be tracked:

This is the continuous integration server status screen, showing that there is a problem with some of the customer's domains.

This is the error output from the system that is displayed by the CI system.

********************************************************************************
* Projects have expiring or broken resources!!!
*
* To update the resources connect to http://dci:7171/
*
* There should be a process running that automatically updates expiration dates
* by running rake expirables:update
********************************************************************************
Project   : Smithee
--------------------------------------------------------------------------------
Developer : AO
Customer  : Tom Tim
SCM URL   : 

Domain Name: xyzzy.com
  Expires: 2012-07-08
  Broken : YES
  Reason : Nameserver [NS1.T324.COM] did not provide an A record: 
  DNS result has no information for xyzzy.com
Nameserver [NS3.T324.COM] did not provide an A record: 
  DNS result has no information for xyzzy.com


Project   : Foo Bar
--------------------------------------------------------------------------------
Developer : AO
Customer  : Xyzzy Myxlplic
SCM URL   : 

SSL Certificate: app.gooblegabble.com
  Expires: 2009-07-16
  Broken : NO
  Reason : 

Project   : Anon site
--------------------------------------------------------------------------------
Developer : AO
Customer  : XYZZY MYXLPLIC
SCM URL   : 

Domain Name: XYZZY.org
  Expires: 2011-01-31
  Broken : YES
  Reason : Nameserver [NS2.DREAMHOST.COM] did not provide an A record: 
  DNS result has no information for XYZZY.org


rake aborted!
Test failures

What you can learn in 30 minutes: user interviews

Jeff Patton and Lane Halley ran an Agile UX workshop for Atomic Object recently. One part of the workshop was the practice of user and stakeholder research through interviewing. For the exercise, we used a speculative development project that we’ve been researching for six months. We interviewed our office manager, Mary O’Neill, as the basis for one of the main personas for the project.

I didn’t expect to learn much from this interview about the project itself. After all, I’ve been thinking about this project, researching the opportunity, discussing it with many people, pitching investors, collaborating with our partners, and generally immersing myself in it for the last six months. On top of that, our office manager is also my wife, so I live with the interviewee and know very well her habits, goals and objectives concerning the subject of the project. How much more could I learn from a 30 minute interview? Lots, it turned out.

Interviewing Mary to craft a persona representing a user of the application we’re building turned out to be a rich vein of insights, surprises, new ideas, concerns and hidden opportunities. This experience brought home the value of the pair interviewing technique Lane taught. It also furthered my resolve to help our clients understand the value of this research. Learning so much, with so little effort, on something I already knew so well – how can this not be part of every project we do?

"Epic" stories

Carl Erickson recently wrote about the rich life of stories at Atomic Object. In addition to our vanilla user stories, we also create “Epic” stories.

We use Epics as large placeholders for some number of user stories. Epics are written at a very high level, and do not contain much detail. We want to record a general understanding of what the customer is looking for, without having to dig into all of the details.

Epics will typically have very large estimates. One Epic could potentially be estimated to take many iterations to implement. By using Epics to put rough estimates on all of the customer’s desired functionality we can provide them with a better idea of the size of the project, without having to go into great detail in up-front requirements work.

When the customer prioritizes an Epic towards the top of the backlog, it is broken down into smaller user stories. We make no attempt to restrict that the total number of points from the resulting stories adds up to the previous estimate of the Epic. This allows more flexibility in granular story estimates and ultimately better accuracy in the final backlog.

Using Epics in this way at the beginning of a project we can quickly provide our customers with the information they need to budget, schedule, and prioritize. We defer spending the time necessary to gain a deeper understanding of the requested functionality until it is more likely that the functionality is actually going to be needed.

In-house UX Workshop

Over the last 18 months, Atomic has made significant growth in our product development services. We now kickoff most projects with an initial discovery and design phase. In this phase we grow our understanding of the client's domain and their vision of the software they want to build. We create artifacts like financial models, personas, wireframes, visual mockups, prototypes and release plans. Eventually, we create a backlog of estimated stories that the development team can start implementing in the development phase.

A few developers at Atomic have shifted into a UX role. We've been growing the company's awareness of UX and product development practices. Last Thursday and Friday Atomic held an in-house UX workshop led by Lane Halley and Jeff Patton. The workshop exercises focused on a speculative development opportunity Atomic has been considering.

Thursday morning started by Lane and Jeff asking what we wanted to get out of the the workshop. They built a backlog of objectives for the workshop and included what we wanted to learn.

Once the schedule was set, we started by talking to the project's stakeholders about their general ideas for the project. Lane and Jeff gave a sample stakeholder interview with one of the stakeholders. We were taught some interview techniques and had the opportunity to practice interviewing with partners. We had some interview candidates lined up for the afternoon so we put together an outline of interview questions.

After Thursday's lunch we slimmed down to a smaller group. This group refined and organized the interview outlines. We conducted four interviews as pairs while the rest of the group observed. We had a retrospective after each interview where we compared notes and critiqued the interviewers.

On Friday the whole company came back together and the interviewers shared their notes. The notes were taken on cards. The cards were laid out and grouped together based on observational similarity. We constructed provisional personas (obviously real personas would require more research) from our notes. Each persona had a name, attributes, objectives and values. We broke out into teams and constructed context scenarios for each persona.

After Friday's lunch, Jeff had our product stakeholders define their high level business goals. We discussed what would constitute success. The discussion gave us insight into what kind of metrics the software may need to provide.

Then Jeff asked how we define a user story. This was a rich discussion that gave us insight into how much we have internalized user stories and how the simple definition of a story has changed over time

After our user story discussion, we extracted activities from our context scenarios. Jeff led us through an exercise where we created a story map. We organized our activities and derived stories from the activities. We looked to the project stakeholders for story validation. We then engaged the stakeholders in release planning using the story map. The exercise showed a great way to prioritize high level stories before fully defining the details required of a finer grained story to be entered into a development backlog.

The workshop ended with a retrospective where the entire company asked questions and shared thoughts.

I'm very excited to see Atomic continue to grow in our UX and product development practices. We've stayed at the forefront of agile development and management for some time. While there, we saw that our clients needed help in formalizing what their project was, who it was for and what business value it would provide. That need and our drive for excellence has pushed us forward to better help our clients. We are enjoying the ability to provide the necessary up front services that ensure we are developing successful products.