Friday, January 3, 2014

The 7 habits of highly successful projects

Each time we offer at UC Berkeley based on the Engineering Software as a Service curriculum, students complete a substantial open-ended course project in teams of 4 to 6.  Each team works with a nonprofit or campus business unit to solve a specific business problem using SaaS; the student organization Blueprint helps us recruit customers.

Besides creating screencasts demonstrating their projects, students also participate in a poster session in which we (instructors) ask them to reflect on the experience of doing the project and how the techniques taught in the class helped (or didn't help) their success.

Virtually all project teams praised BDD and Cucumber as a way of driving the development of the app and reaching agreement with the customer, and all agreed on the importance of the proper use of version control. Beyond that, there was more variation in which techniques different teams used (or wished in retrospect that they had used). Below is the most frequently-heard advice our students would give to future students doing similar projects, in approximate order of popularity (that is, items earliest in the list were independently reported by the largest number of project teams).

  1. Reuse, don't reinvent. Before coding something that is likely to be a feature used by other SaaS apps (file upload, capability management, and so on), take the time to search for Ruby gems or JavaScript libraries you can use or adapt. Even two hours of searching is less time than it takes to design, code and test it yourself.

  2. Start from a good object-oriented design and schema. Taking time to think about the key entity types (models), relationships among them, and how to capture those relationships in a schema using associations and foreign keys. A good design reduces the likelihood of a painful refactoring due to a schema change.

  3. Weekly meetings are not enough.  Especially with the larger 6-person teams we used in the Fall 2013 course offering (237 students forming 40 teams), a 15-minute daily standup meeting helped tremendously in keeping everyone on track, preventing conflicting or redundant work, and informally sharing knowledge among team members who ran into problems. Teams that met only once a week and supplemented it with online chat or social-networking groups wished they had met more often.

  4. Commit to TDD early.  Teams that relied heavily on TDD found its greatest value in regression testing: regression bugs were spotted immediately and could be fixed quickly. Teams that didn't commit to TDD had problems with regression when adding features or refactoring. Teams that used TDD also noted that it helped them organize not only their code, but their thoughts on how it would be used ("the code you wish you had").

  5. Use a branch per feature. Fine-grained commits and branch-per-feature were essential in preventing conflicts and keeping the master branch clean and deployment-ready.

  6. Avoid silly mistakes by programming in pairs.  Not everyone paired, but those who did found that it led to higher quality code and avoided silly mistakes that might have taken extra time to debug otherwise.

  7. Divide work by stories, not by layers.  Teams in which one or a pair of developers owned a story had far fewer coordination problems and merge conflicts than teams that stratified by layer (front-end developer, back-end developer, JavaScript specialist, and so on) and also found that all team members understood the overall app structure better and were therefore more confident when making changes or adding features.

There you have it—the seven habits of highly successful projects, distilled from student self-reflections from approximately sixty projects over two offerings of the course.  We hope you find them helpful!

Wednesday, January 1, 2014

Sinatra or Rails for "Engineering Software as a Service" MOOC and book?

Happy new year!

Thanks to all the students and instructors who are trying out our Engineering Software as a Service book or MOOC.

We are trying to improve the ESaaS book and course in two ways, and want your opinions.

  1. Survey results of Berkeley students show that many of them still have some trouble with Rails by the end of the 14-week semester.  For a few it's magic; for the majority, it's at best hit-and-miss, with some parts they understand and some they don't.  Some of our SPOC instructors using the material have reported similar problems.

  2. For potential instructors, having to learn Rails well enough to be able to answer questions about it is likely an obstacle to adoption.

Dave and I were discussing the following question:  if the essence of 169.1x is to understand how the BDD/TDD cycle works in the context of SaaS, could we do that whole class using only the simpler Sinatra framework, with Rails making its first appearance in 169.2x (or in the second half/final third of a full-semester course)?  If so, should we modify the book accordingly?

I've been putting a good bit of time into figuring out the nuts and bolts of what this would entail.  Here's where I am right now, which is an impasse so i want feedback.

TL;DR: Sinatra is likely a good on-ramp, but trying to use it for all of Cs169.1x does not remove Rails's complexity - it just replaces it with a slightly smaller amount of complexity that has to be hand-built.  (That may not necessarily be bad, and has to be weighed against the "instructor obstacle" argument.)

Note that much of what I say below sounds like I'm listing shortcomings of Sinatra, but I'm not: it was designed to be just as it is, because it serves a fundamentally different purpose from Rails.  Our question here is whether that purpose suffices for teaching SaaS basics.

Sinatra is OK for illustrating basic SaaS concepts in an app with no persistent models. These concepts include important SaaS fundamentals like SaaS architecture, client-server, routes, RESTfulness, template views and view rendering, layouts to DRY out views, form submission, redirects, gathering parameters from either a RESTful URL or a form, and sessions & cookies.

It is slightly less intuitive to debug than Rails: if you make a change to a Rails app while it's running (in development), the change takes effect immediately; with Sinatra you need to use a different gem (rerun) to get this behavior.

Sinatra has no architectural opinion: unlike Rails, it doesn't force MVC or any other architecture (save that by default it expects view templates to be in a views directory).  In my view, for beginning students, this is a deficiency.  You have to impose whatever architecture you think is appropriate, pick your own directory structure to reflect it, etc.  So you can nominally talk about MVC and talk about putting the "real app logic" into models vs. into the main Sinatra app file (which can be said to resemble a controller).  But there's only so much you can do without persistent models.

To add persistence, you need to add either ActiveRecord or DataMapper, and because Sinatra doesn't rely on convention over configuration to determine where things go, a fair amount of manual configuration is needed either way.  We could provide boilerplate that performs that configuration, but it's not clear if such boilerplate would be any less "magic" than what is built into Rails to do those tasks now.

Sinatra seems serviceable for illustrating BDD+TDD with Cucumber/Capybara/RSpec/Autotest, but only barely. Unlike Rails, to get Sinatra to work well with these tools, you have to manually configure several things that happen automatically with Rails as a result of gems like cucumber-rails, rspec-rails and so on.  Autotest in particular seems immature with Sinatra, and a big part of convincing students to actually use BDD and TDD (in my view) is that with Autotest running you don't have to "interrupt" your coding just to run tests.  This could probably be fixed, but again, we'd end up providing some boilerplate that is impenetrable Ruby for a beginner, whereas Autotest works with Rails 'out of the box'.

The difference is most visible when creating a new app (and we informally had already decided it would be better if students got to create & deploy several small apps by the time they start a big project);

  • Rails:  rails new <appname>, rails generate cucumber:install, rails generate rspec:install, rake db:create

  • Sinatra: manually create a directory structure, config files, database, and other glue needed for Cucumber/Capy and Rspec.  In Rails, the idea of a separate environment & database for testing vs. development is baked in; in Sinatra, so you'd have to manually create code that does that as well, and manually modify Rake tasks (eg) so that tests run against the testing database but the app runs against the development database.

An alternative to trying to use Sinatra for all of 169.1x might be to do a single assignment based on Sinatra in conjunction with the SaaS Architecture chapter, then jump into Rails but simplify the presentation at first. There are a few ways to do this:

What we do nowWhat we could do that's simpler
Apps with persistent modelsApps whose models don't use the database at first (but use ActiveRecord later)
Resource based routes (resources :movies) which creates lots of distinct action routesSimple string-match routes  (get '/movies', 'MoviesController#index') that map one-to-one onto Ruby methods in the controller
Rely on Convention over Configuration for rendering viewsRender view explicitly from each controller action (render :template => 'index.html.haml', :layout => 'application.html.haml') and later reveal that these are default behaviors and the explicit call can be omitted

Under this model, the main changes to the course/book would be:

  • HW 1: instead of Ruby-only, do a Sinatra app

  • HW 1.5: instead of Ruby-only, do a simplified Rails app

  • HW 2 and later stay more or less the same, possibly with more scaffolding and pointers to Sam's excellent video tutorials to help complete them

Your turn to comment...what combination will best improve student understanding and increase instructor adoption?