Friday, April 23, 2010

ASP.NET MVC vs. Rails3

I recently was contacted to implement an ASP.NET MVC application and I saw this as a great opportunity to compare it with Rails3.
What immediately strikes you when you start with ASP.NET MVC is how similar it is to Rails. No one can steal ideas like Microsoft!
Rails ASP.NET MVC Purpose (if not obvious)
/app/models /Models
/app/controllers /Controllers
/app/views /Views
/public/javascript /Scripts
/public /Content
/db /App_Data Database data, such as migrations, and models.
/test (unit, functional, fixtures, performance) Separate VS projects
/config /Global.asax, /Properties, Web.config Configuration

Project Generation

Both Rails and ASP.NET MVC relies on code generation to get you started, but the methods they use are different.
Rails uses the command line, which is natural since the Rails approach is to not rely on anything but a good programming editor and the command line.
$ rails tapir
      create  .gitignore
   ...
      create  app/controllers/application_controller.rb
      ...
      create  app/views/layouts
      create  config/database.yml
      create  db/seeds.rb
   ...
      create  public/javascripts/application.js
   ...
      create  test/unit
   ...
ASP.NET MVC uses Wizards inside Visual Studio, which is also expected since Microsoft has a long history of IDE-centric application building.
Worth noting is that I didn't really want to use VS Unit Test, since I normally prefer NUnit, but after one hour of googling and testing I gave up and went with VS Unit Test anyway.

Environment

When Rails is installed it comes pre-configured with three different environment, development, test and production. It is as easy as writing RAILS_ENV=test to switch from the default development environment to the test environment. In this area Rails really shines. There is nothing similar in ASP.NET MVC.

The Model

Rails Model

Rails comes pre-configured with an ActiveRecord model, if you don't write otherwise. If you want to use something else it is very easy. Supported frameworks are among others, Neo4J, MongoDB, and DataMapper.
To create a model in Rails3 you use a command line generator. The generator generates a model, a migration and tests. And you can, of course, choose what kind of model you wish to generate (-o), as well as what kind of testing framework you want to use (-t). Here I just go with the default:
$ rails g model customer name:string email:string
      invoke  active_record
      create    db/migrate/20100419094010_create_customers.rb
      create    app/models/customer.rb
      invoke    test_unit
      create      test/unit/customer_test.rb
      create      test/fixtures/customers.yml

Rails comes preconfigured with Sqlite3, and you don't have to do anything to configure the default database. Moreover, the configuration is set up to use three different databases, one for each environment.
Rails, performs all database changes through scripts, migrations. This is invaluable when you want to upgrade a database that is in production, or when multiple developers are changing the same database model. The migration scripts allow seamless migrations between the different databases.
# An example of a migration
class CreateCustomers < ActiveRecord::Migration

  # Called when migrating up to this version
  def self.up
    create_table :customers do |t|
      t.string :name
      t.string :email
      t.timestamps
    end
  end
  
  # Called when migrating down from this version
  def self.down
    drop_table :customers
  end
end

To move between the different versions of the database we use the rake db:migrate command.
# Migrate to the latest version
$ rake db:migrate 

# Migrate to a specific version
$ rake db:migrate VERSION=20080906120000 

# Rollback one version
$ rake db:rollback 

# Rollback three versions
$ rake db:rollback STEP=3

Read more about migrations in the Migrations Guide

ASP.NET MVC Model

ASP.NET MVC is not a full stack framework and it does not come preconfigured with a model. It is possible to choose between many solutions, such as NHibernate, Entity Framework or LINQ-to-SQL. I choose LINQ-to-SQL because I think it is an elegant, lightweight OR-Mapper, that is easy to work with. Too bad, it isn't prioritized by Microsoft.
Unfortunately, there is nothing like migrations in LINQ-to-SQL. So I use the LINQ-to-SQL design tool to design the classes.

You can then create the database from the model. It is also possible to do it the opposite way, to create the database first and then generate the model, but I prefer this way.
// Creating a database from a LINQ-to-SQL DataContext
public void CreateDatabase(bool force)
{
    var db = new TapirDataContext(@"c:\tapirdb.mdf");
    if (db.DatabaseExists() || force)
    {
        Console.WriteLine("Deleting old database...");
        db.DeleteDatabase();
    }
    db.CreateDatabase();
}

As you understand this is not a viable solution for migrating a production database, but it is good enough (almost) for development and testing, since I can allow myself to recreate the database every time. Once I get a larger database, the time it takes to set it up will force me to go with a better way. The lack of migrations hurts.

Query Language

Rails3 uses AREL, Active Record Relations, and LINQ-to-SQL uses LINQ (surprise!). They are both beautiful solutions and remarkably similar. Both solutions create lazy, composable queries, that are not executed until the latest possible time, when a result is needed. In LINQ, you can see Eric Meijer's Haskell shining through like magic, and in AREL, the beauty of Ruby.
# A simple query with AREL
User.where(users[:name].eq('Anders')).order('users.id DESC').limit(20)

// The same with C#
// Lambda Syntax
db.Users.where(u => u.Name == "Anders").orderBy(u => u.Id).Take(20)

// LINQ Syntax
(from u in db.Users
where u.Name == "Anders"
orderby u.Id descending
select u).Take(20);

One thing that you don't get with LINQ-to-SQL is all the methods that are dynamically created, by need, in Rails, such as find_by_name, find_by_name_and_age, etc.

The Controller

In ASP.NET MVC, it is easy to create a controller, you just right-click on the controllers folder and select Add > Controller. You then get a dialog where you can type in the name of the controller and, optionally, if you like to create default methods for the standard CRUD scenario, just select the checkbox.


public class CustomersController : Controller {
      // GET: /Customers/
      public ActionResult Index() {
          return View();
      }

      // GET: /Customers/Details/5
      public ActionResult Details(int id) {
          return View();
      }

      // GET: /Customers/Create
      public ActionResult Create() {
          return View();
      }

      // POST: /Customers/Create
      [HttpPost]
      public ActionResult Create(FormCollection collection) {
          try {
              // TODO: Add insert logic here
              return RedirectToAction("Index");
          } catch {
              return View();
          }
      }
}

As you can see the code generated is simple and clear. That is the benefit of MVC, simpler models, views, and controllers.
The automatic generation of code, that will probably be changed later is commonly known as scaffolding.
Scaffolding is a temporary structure used to support people and material in the construction or repair of buildings and other large structures. --Wikipedia
Scaffolding is not meant to be used as is. It is meant to get you started!
In Rails3 you add controllers with a generator. You have a lot more options, below are just a few of them.
$ rails g controller
Usage:
  rails generate controller NAME [action action] [options]

Options:
  -e, [--template-engine=NAME]  # Template engine to be invoked
                                # Default: erb
  -t, [--test-framework=NAME]   # Test framework to be invoked
                                # Default: test_unit

As you can see it is possible to choose what template-engine you want to use, and exactly what actions you want to create, the view templates are created automatically with every action you create.
$ rails g controller Customer index create
    conflict  app/controllers/customer_controller.rb
      create  app/controllers/customer_controller.rb
       route  get "customer/create"
       route  get "customer/index"
      invoke  erb
      create    app/views/customer
      create    app/views/customer/index.html.erb
      create    app/views/customer/create.html.erb
      invoke  test_unit
    create    test/functional/customer_controller_test.rb
      invoke  helper
    create    app/helpers/customer_helper.rb
      invoke    test_unit
    create      test/unit/helpers/customer_helper_test.rb


If you want to create all the default actions you should instead invoke the scaffold_generator.

$ rails g scaffold_controller
Usage:
  rails generate scaffold_controller NAME [options]

It will create all the default actions and the views that go with them.
The generated code is shown below. Again, you see the similarity between the two solutions. Rails, automatically generates support for both HTML and XML, making it easy to consume the data from other clients.
class FishController < ApplicationController
  # GET /fish
  # GET /fish.xml
  def index
    @fish = Fish.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @fish }
    end
  end

  # GET /fish/1
  # GET /fish/1.xml
  def show
    @fish = Fish.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @fish }
    end
  end

  # GET /fish/new
  # GET /fish/new.xml
  def new
    @fish = Fish.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @fish }
    end
  end

  # GET /fish/1/edit
  def edit
    @fish = Fish.find(params[:id])
  end

  # POST /fish
  # POST /fish.xml
  def create
    @fish = Fish.new(params[:fish])

    respond_to do |format|
      if @fish.save
        format.html { redirect_to(@fish, :notice => 'Fish was successfully created.') }
        format.xml  { render :xml => @fish, :status => :created, :location => @fish }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @fish.errors, :status => :unprocessable_entity }
      end
    end
  end
...
end

Filters

In Rails it is easy to create filters, that can be applied to specific actions.
class ItemsController < ApplicationController
  before_filter :require_user_admin, :only => [ :destroy, :update ]
  before_filter :require_user, :only => [ :new, :create]
end

In ASP.NET MVC, the same thing can be accomplished by overriding OnActionExecuting, in the controller.
override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var action = filterContext.ActionDescriptor.ActionName;
    if (new List<string>{"Delete", "Edit"}.Contains(action)) {
        RequireUserAdmin();
    }
    if ("Create".Equals(action)) {
        RequireUserAdmin();
    }
}

The functionality may also be extracted into an ActionFilter, which can be applied to the controller or an individual method via an Attribute.
[RequireUserAdmin("Delete", "Edit")]
[RequireUser("Create")]
public class CustomersController : Controller

This is a pattern that can be seen everywhere, when Rails uses Class Macros, a term from Meta-Programming Ruby, ASP.NET MVC uses attributes. And this is appropriate since it usually constitutes meta-data.

Routing

The Routing in both Rails and ASP.NET MVC is incredibly flexible.

Rails Routing

In Rails it is possible to put constraints right in the routing table on just about anything.
# config/routes.rb
Tapir::Application.routes.draw do |map|
  resources :animals

  get "customer/index"
  get "customer/create"
  
  match "/:year(/:month(/:day))" => "info#about", 
 :constraints => { :year => /\d{4}/, 
  :month => /\d{2}/, 
  :day => /\d{2}/ }  
  match "/secret" => "info#about", 
 :constraints => { :user_agent => /Firefox/ }     
end


As you can see Rails comes pre-configures with RESTful routing, and it is indeed the recommended way to set up your routes in Rails. To see what the routing result is is easy:
$ rake routes
                GET    /animals(.:format)          {:action=>"index", :controller=>"animals"}
        animals POST   /animals(.:format)          {:action=>"create", :controller=>"animals"}
     new_animal GET    /animals/new(.:format)      {:action=>"new", :controller=>"animals"}
                GET    /animals/:id(.:format)      {:action=>"show", :controller=>"animals"}
                PUT    /animals/:id(.:format)      {:action=>"update", :controller=>"animals"}
         animal DELETE /animals/:id(.:format)      {:action=>"destroy", :controller=>"animals"}
    edit_animal GET    /animals/:id/edit(.:format) {:action=>"edit", :controller=>"animals"}
 customer_index GET    /customer/index             {:controller=>"customer", :action=>"index"}
customer_create GET    /customer/create            {:controller=>"customer", :action=>"create"}
                       /:year(/:month(/:day))      {:year=>/\d{4}/, :month=>/\d{2}/, :day=>/\d{2}/, :controller=>"info", :action=>"about"}
                       /secret                     {:user_agent=>/Firefox/, :controller=>"info", :action=>"about"}


ASP.NET MVC Routing

ASP.NET MVC Routing is not quite as easy, but everything is possible. The standard routing with paths and parameters works easily out of the box.
// Global.asax.cs
 public class MvcApplication : System.Web.HttpApplication { 
 public static void RegisterRoutes(RouteCollection routes) {  
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
  
  // Constrained route
  routes.MapRoute( "Product", "Product/{productId}", 
   new {controller="Product", action="Details"}, 
   new {productId = @"\d+" } ); // Constraint
  
  // Route with custom constraint, defined below 
  routes.MapRoute( "Admin", "Admin/{action}", 
   new {controller="Admin"}, 
   new {isLocal=new LocalhostConstraint()} ); 
 }
...
}

public class LocalhostConstraint : IRouteConstraint { 
 public bool Match ( HttpContextBase httpContext, Route route,
   string parameterName, RouteValueDictionary values,
   RouteDirection routeDirection ) 
 { 
  return httpContext.Request.IsLocal; 
 } 
} 

As you can see, you have the same power in the ASP.NET MVC Routing, but it requires you to add constraints separately.

Views

In both Rails and ASP.NET MVC, the views are automatically matched in the directory with the same name as the controller, and the file name is named by the action.
app/controllers/customer_controller.rb
app/views/customer
app/views/customer/create.html.erb
app/views/customer/index.html.erb

In Visual Studio it is possible to scaffold a view based on a model, just as it is in Rails. Rails is, of course, more flexible, allowing me to scaffold all or some actions at once. In Visual Studio, it is only possible to do one at a time.

Partials

In both Rails and ASP.NET MVC, a partial is a file that contains a part of a HTML file. The file is written in ASP in ASP.NET MVC and in Rails it is possible to pick the templating language of your choice. Default is ERB, but HAML is also a popular choice.
<!-- Rails -->
<%= render 'form' %>

<!-- ASP.NET MVC -->
<% Html.RenderPartial("Form", Model);%>

In ASP.NET MVC version 2, the preferred way is actually to use two alternate methods to generate the code. They have the advantage that they are type-safe (if that gives you pleasure) and they keep track of nesting.
<%= DisplayFor("Address", m => m.Address ) %>

<%= EditorFor("Address", m => m.Address ) %>

(For Rubyist, the m => m.Address is not a hash key-value expression, but an anonymous function.)

Helpers

A helper is similar to a partial, but it is not a template, it's a method that generates code.
In Rails you create a helper by creating a simple method.
module ApplicationHelper
   def label target, text
     "<label for='#{target}'>#{text}</label>"
   end
 end

And so you can in ASP.NET MVC.
public class LabelHelper {
    public static string Label(string target, string text) {
        return String.Format("<label for='{0}'>{1}</label>", target, text);
    }
}

Conclusion

All in all, ASP.NET MVC with LINQ-to-SQL, is not a bad experience. Eric Meijer and friends have managed to overcome a lot of the hurdles of static typing. They also, let go of it in cases, where dynamic typing is obviously better, such as in the routing and when populating objects from forms. The lambda expressions and the type inference also removes a lot of the boilerplate common in statically typed languages.
Rails is of course a lot more flexible, and faster to work with. The scaffolding is faster and more flexible, the migrations are, as I said before, invaluable when it comes to move between versions.
But, if I'm thrown out of the dynamic Garden of Eden, I'd rather pick my static apples in Redmond, than in California.

Wednesday, April 14, 2010

Static Typing is the Root of All Evil

I can't take credit for this statement, since I am only drawing the logical conclusion from a statement said by a man far smarter than I.

Donald Knuth wrote in his famous paper Structured Programming with go to Statements (PDF):

We should forget about small efficiencies, say about 97% of the time: pre-mature optimization is the root of all evil

Since compilation is premature optimization, it is therefore, the root of all evil. Simple!

Static typing, weak typing, dynamic typing, strong typing, WTF?

There are a number of misconceptions concerning typing.

The meaning of weak and strong typing, is not clearly defined but is mostly interpreted like this.

In a weakly typed language, such as C, the types of the variables can be automatically converted from one representation to another, while in a strongly typed language an explict conversion is required.

This is not what I am writing about here. I am referring to static vs. dynamic typing and the definition I am using is:

  • Static typing, the type checking is performed at compile-time.
  • Dynamic typing, the type ckecking is performed at run-time.

Development Mode

Every time I build my code, my entire code base is type-checked. EVERY TIME! When I am working in development mode, I only care about the method and class that I am currently working on. As long as my tests pass I am happy, type-safe or not.

In development mode it is actually more accurate to call static typing pre-mature de-optimization than pre-mature optimization.

Production Mode

The only reason, to ever use static typing, is because the code may be optimized better, to give better performance.

So, the strategy should be:

  • Use a dynamically typed language during development. It gives you faster feedback, turn-around time, and development speed.
  • If you ever get lucky enough to have performance problems, which cannot be solved with caching or algorithmic optimization, rewrite the problematic code in a statically typed language.

This is what Twitter did, and it has worked well for them.

Other Misconceptions

Proponents of Java often have the misconception that dynamically typed languages are unsafe, yet they use frameworks, like Spring, and Qi4J, that are littered with reflective code, and without the so-called safety net that static typing is believed to give. The reason these frameworks are popular is because they allow us to be more productive. The reason they are more productive is because they use dynamic programming techniques.

Anyone, who has worked seriously with a modern dynamically typed language like Ruby or Smalltalk, know that they are more productive. Working with waterfall languages after working with agile languages is just painful. (Thanks to Andreas Ronge for coining the term Waterfall Language.)

Sunday, February 14, 2010

Why not Maven

This blog post is in reply to a comment by Jason van Zyl on my previous post. It is written as a post since blogger does not allow me write syntax highlighting in comments.

Jason, you are probably right about me making assumptions about what Maven is, or at least what I think that it should be. But the fact is that, my main use of Maven is as a build tool!

And don't misunderstand me, I really like a lot of what Maven has brought to the community. The standardized repositories and the standardized project layouts being the main contributions.

I also like the fact of the one-click build, or one-click development environment setup, even though I don't think that these are an invention of Maven. I know that at least Joel Spolsky wrote about this years ago.

But what I don't like is that, whenever I want to do something that is not part of the standard Maven toolset, I have to invoke the Google Gods, and hope that someone has written a halfway decent plugin that does the work I want done. Most of the time, the plugin is OK and I end up using it. But, with every little thing I add, the project setup gets a lot more complicated and inconstant.

Below is an example of a configuration for generating documentation with asciidoc. I really think that the readability of the Maven part obscures what I want to do. And being obscure is one of the primary sins of programming and building in my book.

<!-- Generating a file with asciidoc and Maven -->
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.1.1</version>
    <configuration>
        <executable>asciidoc</executable>
    </configuration>
    <executions>
        <execution>
            <id>generate-deployment</id>
            <configuration>
                <commandlineArgs>
 --unsafe --out-file
${project.build.outputDirectory}/deployment.html
${project.build.directory}/deployment.txt
  </commandlineArgs>
            </configuration>
            <goals>
                <goal>exec</goal>
            </goals>
            <phase>compile</phase>
        </execution>
    </executions>
</plugin>


# Generating a file with asciidoc and Rake or Buildr
file ${project.build.outputDirectory}/deployment.html => 
 ${project.build.directory}/deployment.txt do
 sh "asciidoc --unsafe --out-file=#{to} #{from}"
end

The example above is just one, of many, that I have run into, during my years of dealing with build systems.

I took a look at Polyglot, and it is not the answer to my annoyances. My main annoyance is not the XML, it is that I cannot do simple things without having to resort to third party plugins that don't work the way I want them to. I want precision and Maven does not give me that. (Perhaps because I did not write it.)

I'm also sorry if I sounded like I thought it was easy to do what you are trying to do. I don't! I have tremendous admiration for your abilities and for what you are trying to do, but as I said in the blog post. I think you are wrong when trying to create a build system that does not include the ability to use programming abstractions. The abstractions lets me do simple thing easily.

Perhaps the problem is that Maven is trying to do to much. Perhaps there should be two parts, Maven-Project for generating nice project information about the status of a project, and Maven- Build for dealing with the part that is doing the building. In my opinion Maven-Build should be more like Rake and Buildr and less like what Maven is at the time of writing.

But you don't have to agree with me, you obviously have a different experience than I do. In the mean time I will continue to use Rake and Buildr, and continue to use the paved road that Maven has provided with its standardized repositories and layouts.

Saturday, January 23, 2010

Maven, the new Elephant on the Block

Some of you may remember the article, by Bruce Tate, Don't Make Me Eat the Elephant Again.
It was an article about EJB, and Bruce was begging Sun not to make the same mistakes with EJB3 as they had done with EJB, and EJB2. They didn't, Spring came along as better alternative and forced EJB3 to become slimmer and better. If not for Spring, EJB3 would probably look very different from what it looks like today.
Well, guess what, there is a new elephant on the block and its name Maven2.


Just like EJB2, Maven2 was born out of something so unbearable that anything else was bliss. Jelly anyone!


But just as EJB was fundamentally flawed, so is Maven2. Build systems, even advanced ones like Maven is fundamentally about two things.
  1. Check if something that something else depends on has changed
  2. If so, do something.
That's it, that is what is important.
The checking may contain various sophisticated methods for detecting if files, subsets of files, all files, web pages, twitter feeds, etc, has changed, but that is really it.
And the doing can be anything, Copy files, commit files, build websites, run tests, generate code, launch missiles, whatever!
But the key to doing this efficiently is a programming language with easy access to system commands and the ability to create simple abstractions, with methods, variables, and objects. The language should also, preferably, be one without a lot of ceremony, like Ruby, Javascript or Python.
There are already build systems like this out there, Buildr, SCons, and Rake come to mind, but they do not have the momentum of Maven, so a merger between Maven and Buildr would be wonderful.



So, Jason, hear my plea, Don't make me eat the elephant again!

Saturday, January 16, 2010

Scripting in Ruby

I just read, or rather skimmed, the book, called Everyday Scripting with Ruby and it is awful. I had high expectations. I was expecting something like Perl for System Administration, where you right away get into hard core Perl scripting. This book is nothing like that! It is a really basic introduction to Ruby, and if you have any experience programming at all, DON'T BUY THIS BOOK.

OK, enough complaining! I am going to do what this book does not do. I am going to show a good way to write scripts in Ruby. If you want to learn more about Ruby, buy Programming Ruby, The Well-Grounded Rubyist and The Ruby Way.

Ruby is an excellent language. It is, in my opinion, the best scripting language and it beats both Perl and Python face down. If you are not scripting in Ruby, you are missing out. Ok, so here is the script.

sendgmail

The script is called sendgmail and it sends mail via gmail (surprise!).

This is how the command is used.

$ sendgmail --help
/usr/local/bin/sendgmail [options] attachments...
Options are ...
    -t, --to TO                      Send email to recipient
    -m, --message MESSAGE            Include the message.
    -s, --subject SUBJECT            Include the subject.
    -v, --verbose                    Log to standard output.
    -V, --version                    Display the program version.
    -h, -H, --help                   Display this help message.

And here is the code, liberally sprinkled with comments.


#!/usr/bin/env ruby
# Use the currently configured ruby version

# optparse contains OptionParser, ostruct: OpenStruct and growl Growl
require 'optparse'
require 'ostruct'
require 'growl'

# This is the name of the script that is called
# Whatever you name your script will be reflected here.
# This can be useful if you want to have the same script do many things
# Create differently named links to the script 
# and use the program name to select behavior.
PROGRAM_NAME = $0
PROGRAM_VERSION = 1.0

# Create an OpenStruct to save the options.
# OpenStruct allows you to use options.my_option instead of 
# option['my_option'] with a normal hash
# http://ruby-doc.org/stdlib/libdoc/ostruct/rdoc/classes/OpenStruct.html
def options
  @options ||= OpenStruct.new
end


# This is the options of the program, see OptionParser
# http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
def program_options
  [
          # The values of the array are,
          # [long_option, short_option and parameter, description, code to execute]          
          ['--to', '-t TO', "Send email to recipient",
           lambda { |value| options.to = value }
          ],
          ['--message', '-m MESSAGE', "Include the message.",
           lambda { |value| options.message = value }
          ],
          ['--subject', '-s SUBJECT', "Include the subject.",
           lambda { |value| options.subject = value }
          ],
          ['--verbose', '-v', "Log to standard output.",
           lambda { |value| options.verbose = true }
          ],
          ['--version', '-V', "Display the program version.",
           lambda { |value|
             puts "#{program_name}, version #{PROGRAM_VERSION}"
             exit
           }
          ]
  ]
end

option_parser = OptionParser.new do |opts|
  opts.banner = "#{PROGRAM_NAME} [options] attachments..."
  opts.separator ""
  opts.separator "Options are ..."

  # Add the command on_tail, to make it appear as the last option in the list.
  opts.on_tail("-h", "--help", "-H", "Display this help message.") do
    puts opts
    exit
  end

  program_options.each { |args| opts.on(*args) }
end

begin
  # Parse the options and remove them from the ARGV array
  option_parser.parse!
rescue OptionParser::ParseError => error
  puts error.message
  puts option_parser
  exit
end

unless options.to
  puts 'Missing required argument --to or -t'
  puts option_parser
  exit
end

options.subject = 'No subject' unless options.subject
options.message = '' unless options.message

# Concatenate the options into a proper command
command = 'sendemail -o tls=yes -s smtp.gmail.com -f from@gmail.com -xu from@gmail.com -xp secret'
command += "-t '#{options.to}' -u '#{options.subject}' -m '#{options.message}' "

# Append the filenames with the -a option to send them as attachments
# Only the non options (the filenames) are left in ARGV
unless ARGV.empty?
  command += " -a #{ARGV.join(' ')}"
end

# Print the command to screen if using verbose mode.
puts command if options.verbose
system command

Growl.notify "#{options.subject}\n#{options.message}", :icon => :jpeg, :title => "Email sent to #{options.to}"

The script requires you to install sendemail. It also requires you to install the growlnotify command line utility that is distributed with Growl and the gem that uses this utility, also conveniently named growl.

Install required dependencies on OS X


# Install sendemail and capabilty to send secure mail, required by Gmail
sudo port install sendemail p5-net-ssleay p5-io-socket-ssl

# Install growl command utility from the mounted image
sudo sh /Volumes/Growl-1.2/Extras/growlnotify/install.sh

# Install the growl gem
sudo gem install growl

That’s it, a simple script that will allow you to send messages and files through
gmail from the command line.

Friday, December 11, 2009

Why #twitter sucks!

The problem with Twitter is information overflow. The reason for this overflow can be summarized with one simple sentence.

What are you doing right now?

That sentence opens up a whole can of worms.

It is totally egoistic. "What are you doing right now?", doesn't say that, what you are writing should, preferably, be interesting to someone else than you.

It doesn't say: You are sharing this with the rest of the world and you should at least think twice before you write "I am eating a donut".

"Right now", is devastating. It encourages you to write something without thinking. If it was to be taken literally, "I am publishing an uninteresting message on Twitter" would be the content of 90 percent of the tweets.

One thing it does not say is "Let's have a public dialog", but that doesn't stop most people from satisfying their IDOL-wannabe-souls from publishing their personal dialogs without thinking twice about spamming everyone else who is following them. Direct messages exists, even within Twitter, if you are not happy with other instant messaging tools or Skype.

So what can be done about Twitter?

Probably nothing since the current behavior is so ingrown in people's minds, it will be impossible to change. But here are a few ideas:

A new better introductory question. Instead of "What's happening?" maybe:

What would you like to share with the world?

Think TED! What's an idea worth spreading? What would you like to share with the world?

It implies that it should be interesting to at least someone else, other than yourself.

It doesn't have to be right now. It is even preferred that it is something that you have been thinking about for a while.

If it is a conversation with someone else, please make it a conversation with someone else, not a public display of your stupidity (or intelligence, if you prefer that)! Use Direct Messages or IM-tools.

If the tweet cannot be said in 140 characters, you have failed. Twitter is a micro blog and if you are not able to say what you want in 140 characters, please write it up in a normal blog and tweet a link it. Sending five consecutive messages on the same topic only shows that you have failed to condense your message down to it's essentials. If it cannot be said in 140 characters, don't tweet it, blog it!

I am aware that not everyone thinks that Twitter sucks and that I am not using it properly. I should not look at old tweets, etc., etc., but I want to use it like this.

Filters

I am therefore working on a filter for filtering out all the crap. The filter goes under the obvious name, no-crap, and it currently contains 4 filters:

  • A Bayesian filter is like a personal spam filter and allows you to classify certain tweets as crap or not.
  • A stop-word filter is a filter, that does not allow tweets with specified words, such as Viagra, to get through.
  • A max-per-time filter stops the tweeters that tweet tons of interesting things, but do it at the expense of others. You can, for example, set this filter to max five tweets per day.
  • A duplicate-per-time filter stops the re-tweet overflow that can happen if something interesting comes a long. It compares the tweet and does not resend tweets higher than a given similarity.

This is currently a hobby project, so it does not get much attention. I invite anybody to steal my ideas, and to merge them into the current Twitter clients.

Sunday, November 29, 2009

Working at Jayway

This morning I woke up singing, like I do most mornings. :D There are so many things ahead of me and most of them I like to do. One of those things is going to work. I worked at Jayway, for five years, three years ago, and I recently came back.

The reason I left was that I wanted to work with one product and one team. I wanted to do everything right, I wanted to use pair-programming, domain-driven design and test-driven development. I had many plans. It didn't turn out that way, the people on the team I worked with did not want to use pair-programming, DDD or TDD! :(

After a few years I gave up and came back to Jayway and I love it. The company has grown quite a bit, while I was away, and that is a good thing. Three years ago I probably thought that it was a bad thing. It isn't! Three years ago we had to come into a company as resource consultants, but now we, many times, come in as a team or get to do the project in-house. This is a really good thing. Getting to work with other Jayway people is a real Joy. They are smart, motivated and pragmatic. If I leave the project for a week, someone steps into my role and, everything works out fine. People and interactions over processes and tools is very, very true!

There are a many reasons why I think it is so nice to work at Jayway.

Natural authority is a pattern from Adrenaline Junkies and Template Zombies that states:

The meaning of an authority is a person who knows a great deal about something. Another meaning, in authority, is a person who is in charge. If someone who is an authority also is in authority, this is a natural authority.

The person who knows best, gets to make the decisions. This is the way it is at Jayway. You are not assigned to a project, you are asked if you want to work on the project and, it is OK to say no.

Management. My wife recently told me about a danish entrepreneur, Lars Kolind. He is usually called in when a company is not doing as well as it should. He talked about something that he called Kolindkuren (the Kolind treatment). What he does in this treatment is that he turns things upside-down. And one specific thing he did was that instead of asking managers who they wanted as employees, he asked the employees who they wanted to have as managers. It turned out that no-one wanted many of the managers.

When I think about this and how this would work out if we did the same thing at Jayway, I don't think that anything would change. I am happy with my managers, I don't see them as managers, I see them as collegues who work to allow me to do the job I want to do. If I got to choose who I wanted to be my boss, I would choose exactly the people we have right now. And, I think we all feel the same way. An example of this came a few years ago.

Thomas, the president of Jayway was fired by the board. What happened then was amazing, one week after Thomas was fired, 90 percent of the employees had resigned. If Thomas can't work for you, then we wont work for you, was the clear message that was sent. And it had effect, Thomas is back, the board is gone, and we are all happy.

Competence is the driving factor of everyone at Jayway. We all have different interests, but the common denominator is that everyone loves programming and want to get better at it. Take a look at this weeks competence workshops, and remember that they are all voluntary!

Competence Calendar

Openness is very important to me. If I know why a decision has been made, I can understand why it was taken even though I may not agree with it. At Jayway all the managers write a short daily mail about what they are doing during the day. Every week Thomas updates the wiki with what is going on currently and what is planned for the future. Everything that does not have to be secret is open and available. If you want to find it, it is on the wiki.

If a doctor wants to chop of my leg I would be happier with the decision if I knew that he wants to do this because I have an incurable tumor in the leg, instead of him wanting to practice his amputation skills.

So, that is how it is to work at Jayway (at least in my mind). If you feel that competence and humbleness is more important than fancy titles, come and join us.

Tuesday, November 24, 2009

Under the Hood of git clone

When you clone a git repository, everything is automatically setup to allow you to fetch, pull, push to and from the remote repository, origin. But what is really going on? git remote is configured with a few lines of configuration in the config file inside the .git/ directory.

Here’s how it works:

Create a new repository, called base, add a file to it, then commit.
$ mkdir base;cd base;git init
Initialized empty Git repository in /Users/andersjanmyr/tmp/repos/base/.git/
$ echo foo > bar.txt
$ git add .
$ git commit -m initial
[master (root-commit) 548d762] initial
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 bar.txt
Clone this repository, called klon.
$ cd ..
$ git clone base klon
Initialized empty Git repository in /Users/andersjanmyr/tmp/repos/klon/.git/
Initialize a new repository, called kopy.
$ mkdir kopy;cd kopy;git init
Initialized empty Git repository in /Users/andersjanmyr/tmp/repos/kopy/.git/
The difference in configuration between the klon and the kopy.
$ diff klon/.git/config kopy/.git/config
7,12d6
< [remote "origin"]
<       fetch = +refs/heads/*:refs/remotes/origin/*
<       url = /Users/andersjanmyr/tmp/repos/base
< [branch "master"]
<       remote = origin
<       merge = refs/heads/master

To set up the newly created repository to work the same way the clone does, all I have to do is to edit this file to make it look the same. This is not what git does, so lets do it the git way.

Fixing the remote configuration.
$ cd kopy
$ git remote add origin /Users/andersjanmyr/tmp/repos/base
This adds the [remote "origin"] entry to the config file.
[remote "origin"]
        url = /Users/andersjanmyr/tmp/repos/base
        fetch = +refs/heads/*:refs/remotes/origin/*
Fetching from the origin adds the remote heads to .git/.
$ git fetch
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Users/andersjanmyr/tmp/repos/base
 * [new branch]      master     -> origin/master
$ find .git/refs
.git/refs/
.git/refs/heads
.git/refs/remotes
.git/refs/remotes/origin
.git/refs/remotes/origin/master
.git/refs/tags

Now I can check out the origin/master, but if I do it the normal way, the configuration will not be set up correctly to allow me to pull and push the way i can with the clone.

Checkout the master version with tracking information.
# DON'T DO THIS, It does not add the tracking information to the config file.
$ git checkout -b master origin/master

# This add the tracking information to the config file.
$ git checkout --track -b master origin/master
Branch master set up to track remote branch master from origin.
Already on 'master'
The following information is added to .git/config when --track is used.
[branch "master"]
        remote = origin
        merge = refs/heads/master

That’s it! Now the .git/config file looks the same as if I had done a normal clone, but lets continue. What do the entries in the config file mean.

Definition of the remote.
[remote "origin"]
        url = /Users/andersjanmyr/tmp/repos/base
        fetch = +refs/heads/*:refs/remotes/origin/*
Definition of the remote as git config commands.
$ git config remote.origin.url /Users/andersjanmyr/tmp/repos/base
$ git config remote.fetch = +refs/heads/*:refs/remotes/origin/*

The first part is just declaring the alias origin for the remote url (or local in this case :).

The second part of the definition is more interesting. It sets up the refspec that will be used if you don’t provide anything on the command line. As we usually don’t provide a full refspec, most people don’t know what it is, this is extremely useful. In case you don’t know, the remote commands of git, push, pull, and fetch take a refspec as their last parameter. It is just that we usually just refer to a small part of it.

Usage of the remote git commands.
git pull <options> <repository> <refspec>...
git fetch <options> <repository> <refspec>...
git push <options> <repository> <refspec>...

The format of a refspec parameter is an optional plus +, followed by the source ref src, followed by a colon :, followed by the destination ref dest.

It defines what dest object should be updated by the src object.

Example definition of refspec.
# The local
# +<src>:<dest>
+refs/heads/spike:refs/remotes/origin/master

In our day-to-day usage of git, we usually don’t use the full syntax of the refspec. Instead we just refer to simple names. Like this.

Day-to-day usage of refspecs.
# Push the local branch to the remote branch with the same name
$ git push origin
# Pull the master into the local master.
$ git pull origin master
# Fetch the master of the origin and put the result in the remote experimental
$ git fetch origin master:refs/remotes/origin/experimental

The above really means:

Definition of the branch, expanded
# Push the local branch to the remote branch with the same name
$ git push refs/heads/*:refs/remotes/origin/*
# Pull the master into the local master.
$ git pull origin refs/heads/master:refs/remotes/origin/master
# Fetch the master of the origin and put the result in the remote experimental
$ git fetch origin refs/heads/master:refs/remotes/origin/experimental

From the above syntax, it is also possible to decrypt the obscure syntax used when deleting a remote branch. Deleting is the same as pushing to a remote branch without giving a local branch.

Delete a remote branch.
# Delete the remote branch serverfix
git push origin :serverfix

Now, we are down to the last part of the configuration, the branch definition.

Definition of the branch
[branch "master"]
        remote = origin
        merge = refs/heads/master

The first part branch.master.remote, tells git to use origin as the default remote, if none is given for this local branch.

The second part tells git which remote branch to use when merging. This also affects pull and fetch. Depending on your settings of push.default, it will also affect push.

Hopefully this has clarified some of the intricacies of git remoting. Just remember that if you make a mistake, you can always fire up an editor and edit the config file directly.

I’ll finish up with some more commands that can be used to get information about the remote.

Additional remote commands, to explore a remote.
# Show all remote branches
$ git branch -r
  origin/cucumber
  origin/customercare-0.6.x
  origin/master

# Show all remotes verbosely
$ git remote -v
origin  /Users/andersjanmyr/tmp/repos/base (fetch)
origin  /Users/andersjanmyr/tmp/repos/base (push)

# Show info about the remote
$ git remote show origin
* remote origin
  Fetch URL: /Users/andersjanmyr/tmp/repos/base
  Push  URL: /Users/andersjanmyr/tmp/repos/base
  HEAD branch: master
  Remote branches:
    experimental stale (use 'git remote prune' to remove)
    master       tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

# List the remote heads of the origin
$ git ls-remote --heads origin
548d7624f5385d36314e8ab61e61e8872c0bfe90        refs/heads/master

That’s it for today.

Tuesday, November 10, 2009

Adrenaline Junkies and Template Zombies

Since I had plenty of time to read on my flights back and forth to OOPSLA, I managed to read through a few books. One of them was Adrenaline Junkies and Template Zombies by Tom DeMarco et al. Being the sceptic that I am, my attitude when starting to read this book was: "Yeah, I know a lot of people has praised this book and I know that Tom DeMarco has written Peopleware, but what the hell do these bozos know anyway!" ;)

I figured that the six of them could probably scrape together a few decent people patterns, but I am a bit fed up with the whole pattern template style, so my expectations were not as high as they should have been.

Anyway, the book is amazing, the pattern template is really lightweight, basically, a name, an image, a statement, and a story. And the stories, are good. The authors have a remarkable ability to observe human nature and also the ability to write about what they see.

Some Patterns

The book contains 86 patterns, most of them are negative since they are funnier, as one of the authors said on a podcast.

Dead Fish

A dead fish is a project, that every one knows is impossible to start out with, but nobody says anything since the culture in the company is that way. If you say something you may get responses like,

Prove it! Prove that it is not feasible that this project will succeed!

Or,

Are you a weenie or a layabout?

Projects like this are usually worked on until it is inevitable that they will be late, and then everybody acts surprised.

Nanny

A nanny is a project manager that is aware of the capabilities of her staff and nurtures them and lets them at their peek. The nanny is responsible for improving her staff and make responsible project citizen of them. A nanny enables workers to do their job.

Eye Contact

If your life depended on a project, would you want them working close together or in different corners of the world? Human communication goes way beyond words, and it is foolish to think that it is possible to work at full speed with people who don't know each other and live in different places.

Dashboards

A dashboard is a highly visible board, that enable the project members and others to get an instant view of the status of the project. It can be a web page or a board on the wall. What is significant with a dashboard is that it is updated as soon as anything important happens in the project. All project members care for the dashboard since that is the place where they can find out how they are doing. Dashboards contain just the right amount of data.

Film Critics

A film critic is a project member or someone related to the project who thinks that they can succeed even if the project is a failure. They may often have good points, but usually when it is too late to do something about it. It is no use to have a person like this on the team, since they are not really on the team if they don't go down with the ship.

Natural Authority

The word authority have several meanings. The meaning of an authority is a person who knows a great deal about something. Another meaning, in authority, is a person who is in charge. A person who is an authority and in authority is a natural authority. This is the healthy pattern.

The White Line

The white line is the line on a tennis court. The line is a clear signal if the ball is in or out. Occasional dispute may arise over a questionable judgment call, but the line is respected by everyone. Most projects don't have this line. Create a white line for your project so that you know what is inside the project scope and what is not.

Silence Gives Consent

If someone don't oppose an idea, it is, usually, taken as consent. Especially from people who come from different working areas, like management and programmers. Silent consent is not good for anyone, because nobody is sure what has been decided and what has not. A way to remedy this problem is to keep a list of commitments where it is written down who has promised who what. The Scrum sprint backlog is similar to such a list.

Time Removes Cards from Your Hand

The earlier a decision can be made, the better. If you know that a project will not make a deadline, the decision to change the scope should be made as early as possible, since that will allow the team to work on the most important features first. If a decision is delayed until it is too late, the decision is made implicitly by time.

Rhythm

Instead of being daunted by overwhelming tasks, projects with rhythm take small, regular steps thus establishing a regular beat that carries them toward their goal.

This journey of a thousand miles begins with a single step --Lao Tzu

False Quality Gates

False quality gates are quality checks that don't do anything to promote the quality of the project. It is a sign that more attention is concentrated on format, rather than content. It can be a glossary of terms with the wrong content or a word template where most of the sections are filled in with the words "This section must contain text to fulfill company guidelines".

Testing Before Testing

Testing before testing refers to the practice of thinking about how to test a feature at the same time you think about how to implement it. If it cannot be tested how can you know that it does what you say?

Cider House Rules

Cider house rules are rules written by someone unconnected to the project. Rules like this, that give no apparent value, are a burden to the project and therefore often ignored.

Nobody pays attention to them rules. Every year Olive writes them up and every year nobody pays any attention to them. -- John Irving, The Cider House Rules

Talk Then Write

The team makes decisions during conversation, and then immediately communicates the decisions in writing. This is a really important pattern that it is easy to forget!. A common place where this kind of decisions are written down is very helpful.

Practicing the End Game

A team that does not release often and regularly will often take a long time to make a release. The act of releasing a product should be practiced often so that is becomes a natural part of the project.

Data Quality

Data quality often sucks! A common solution is often to attack the problem with technology instead of putting in the manpower that is actually needed to improve the quality of the data. Company web-sites are the prime example of this. They are often full of completely useless information.

Undivided Attention

Complex work is hard and it requires undivided attention to be performed properly. Splitting people's attention over multiple project will severely hinder their performance.

By doing two things at once, you've cut your IQ in half, and believe me, those 40 points can really make a difference. -- Dale Dauden, The New York Times

Orphaned Deliverables

Orphaned deliverables are deliverables that no-one values enough to pay for. Always make sure that there is a sponsor for every artifact that you are developing.

Hidden Beauty

Hidden beauty inside a program, that little extra thing, that may seem unnecessary, is what shows that the creator cares about his work. It is not unnecessary! This caring is what separates a quality product from garbage.

I Don't Know

If you are afraid of saying the words, "I don't know!", you are probably working in an organization where saying it means that you will be taken for a fool. In a healthy organization, "I don't know!", means that you don't know, but you will in a while, if it is important enough.

Loud and Clear

Having a clear goal, that everyone agrees to, is vital to a project. Clear goals allows you to focus the project activity if it starts to move away from its purpose. A PAM statement, that contains the Purpose, Advantage, and Measurements of a project is very helpful.

Conclusion

The patterns described above are just my short summaries, and I don't do them justice, but hopefully I have awoken your interest in this wonderful book.

It made my top-ten list and I recommend it to everyone from programmer to president. I like that the authors don't always provide a solution, but instead just describe the way they see it, it is up to you to figure out a solution to the problem yourself.

Saturday, November 07, 2009

The Craftsman Analogy

The analogy of software developers as craftsmen has become very popular. I don't know where it started, but the first book I read about it was the excellent book The Pragmatic Programmer by Andy Hunt and Dave Thomas. I really liked this analogy, it seemed right.

A few years later, Pete McBreen released the book Software Craftsmanship, where he articulates that software development should be more like craftsmanship. Pete writes eloquently about programming masters that should be paid ten times more than their apprentices because they are at least ten times more effective. The masters also have the responsibility to take on journeymen and apprentices to train in their particular flavor of software development. This also rang very true to me.

But, there is something fishy with this analogy. Something ain't right!

I have come to understand that the analogy refers to how it is believed that the craftsman "industry" worked ages ago, rather than how it is today. If you hire a carpenter today he may tell you that he will come "sometime next week". I have rarely been given a time that is more exact than a four hour span, to just meet up.

After the meeting has been scheduled, I have to be very lucky if my craftsman actually appears at the appointed time. Most of the time he will not show up at all!

If I happen to be lucky enough to get a craftsman to show up or to call and tell me that he can't make it, I usually make a note of this guy as being, a highly reliable craftsman, worth hiring again. He gets a golden star for just calling to tell me that he won't come.

When we finally meet up, the craftsman may do a terrific job and I will happily recommend him to anyone, but most likely we will talk and he will tell me that he doesn't have the time to do the job right now, but that he can come back tomorrow. If you let him go with that, he most likely will not come back tomorrow or the next day. He will come back when you call to remind him that he should have come back. And even better, if you pay him in advance you will never see him again, ever!

So, if our goal is to get programmers to be viewed as craftsmen, we're already there. Just change the statement those f***ing carpenters never do what we expect! to those f***ing programmers never do what we expect!

Maybe the whole idea of programmers as craftsmen is just:

It was better in the old days.

It wasn't, it is better now!

I have learned one thing through all my years of programming:

The more I learn, the more I learn how little I know. --Socrates

I'm not a software craftsman, I'm a humble programmer, a good one, and proud of it.

Thursday, October 29, 2009

OOPSLA 2009 Thursday, October 29th

Moving Fast at Scale, Lessons Learned at Facebook, Robert Johnson

Facebook has over 1 million active users per engineer.

Slowing down to get it right is not a good idea, unless you know exactly that your idea is right. If you try things fast, you can try out more things and you can get feedback fast.

How do you get fast?

  • Never block developers
  • Give developers control. At Facebook developers code, test and deploy. No QA is involved.
  • Tune processes for speed
  • No deadlines, but the site cannot go down.
  • Frequent small changes, no delays, easier to isolate bugs.
  • All development in trunk with weekly releases.
  • Deployment tools
  • Gatekeeper
  • Do less work
  • Build tools
  • phpsh -interactive development
  • diffcamp - code review
  • XHProf - profiler
  • Scribe - moves data from server to central repository.
  • Hive - data warehouse infrastructure on top of hadoop
  • Work with open source

How to deal with a lot of users?

  • Scale Horizontally
  • Web Server, the relations have moved to the web layer from the database.
  • Memcache
  • Database, only used as a persistence layer. No joins etc.
  • Thrift
  • C++, Java, Erlang, Python

Open Problems/Future Work Languages for real-time data-access parallel with lots of dependencies. Distributed indexing Automatic clustering of data Profiling of parallel data * Better ways of expressing client and server code + Javascript and PHP are not well matched.

Toward Cloud-Agnostic Middlewares, E. Michael Maximilien

The Cloud Computing Landscape

  • Service Providers
  • Platform Providers
  • Service Brokers
  • Application Providers (SaaS)
  • Users

Challenges in Cloud Computing

  • Data lock-in
  • Application programming lock-in
  • Management lock-in
  • API lock-in
  • IncreaseRisk
  • Security and Privacy
  • Catastrophic failures
  • Business models and impacts

Research Opportunities

  • Cloud middleware
  • Agnostic wit respect to providers, frameworks and interfaces.
  • Learn Best Practices
  • Fluid cloud application deployment
  • Optimize could usage prices
  • Give indication of cloud readiness

Use Cases

  • COBRA Java text analytics solution. (JavaEE)
  • CoScripter application (RoR)
  • JumpStart sMash application (WebSphere, Smash)

The Architecture

  • Core APIs
  • Cloud APIs
  • Cloud Adapters
  • Clouds and Brokers

IBM Altocumulus

Altocumulus is a configuration tool that helps you to deploy your application into different clouds. It supports multiple different service and platform providers. It helps you configure your deployment so that it follows the commonly known "good" practices. You can use it via the Web or via a RESTful API. It also supports Atom and RSS feeds. It is very interesting.

Lessons Learned

  • Cloud Providers need to include some support service s to be enterprise ready
  • Cloud Providers need to expose flexible image creation facilities
  • Cloud virtual machines are mostly hardware based and their memory management is suboptimal
  • Best Practice Pattens works but need to evolve.
  • Standardized hardware is important when implementing cloud infrastructures
  • Instance monitoring capabilities should coma as part of the cloud facilities and APIs
  • The cloud space is still maturing

When Users Become Collaborators: Towards Continuous and Context-Aware User Input, Walid Maalej et al.

User input is critical for the success of projects.

Various types of input:

  • Field observation, Lead Users
  • Perpetual Beta, Legacy Documents, Usage Data
  • Issue and Bug Report, Enhancement Request, Feature Request
  • Workshop, Interview, Survey, Classification Requests

Built in Feedback Mechanisms

Examples: Application Bug Reports, Application Usage Data, Discussion groups

Most applications don't have feedback mechanisms built into them.

Users are motivated by feedback, so if they submit a report and don't get any feedback, they will probably not do it again.