Introducing Context Specification

iStock_000008633650Small Introduction

Context Specification is a lot more than merely a new way to write tests.  Although it was conceived around the same time as Behavior-Driven Development (BDD), and probably inspired by some of the BDD concepts, it is quite different.

The difference between Context Specification and BDD lies in semantics and purpose.  Both are concerned about driving development through behaviors rather than implementation artifacts like classes or components.

BDD frameworks are typically purposed to acceptance testing.  Recently, programmers encouraged customers to participate in requirements specification by writing the acceptance tests with tools like FIT.  Acceptance testing frameworks are built with the assumption that business experts will be writing tests.

Acceptance testing frameworks often presume the user experience will be as rich as it is for the programmer.  However, a programmer’s notion of readability and elegance is usually quote different than a non-programmer’s notion of readability and elegance.  Often, programmers’ notions of readability and elegance will be different among themselves.

The Given/When/Then grammar is a good example of programmer’s presumption of a good user experience.  We have to remember that when we create a tool to be used by non-programmers, we are designing user interaction and user experience.  Programmers are historically the least effective people in creating a good user experience.

Given/When/Then grammar is good for capturing all of the details of a given scenario, but chooses to convey a lot of information in prose.  Using prose for this purpose often leads to run-on sentences that defeat effective communication.

Specifications written with the Given/When/Then grammar are just as likely to be hard to read.  The grammar can be an obstruction to learning, communication, and understanding.

Creating descriptions of specific uses of software is Context Specification’s primary concern.  As with BDD and Test-Driven Development, the descriptions are specifications that end up driving development, and leave behind an automated test suite.

Who Writes Specifications?  Who Writes Tests?

Programmer’s don’t like to write documentation, and business experts don’t like to write tests.  Context Specification is about creating a specifications document with code.  However, it doesn’t presume business experts will be writing the tests.  Programmer’s write the test; that’s what they’re good at; business experts are good at being business experts.

Context Specification’s semantics focus on transforming traditional tests into specifications.  These specifications serve as descriptions and documentation for how a system behaves under the various situations that the system is put into.  Context Specification grammar is designed to make specifications easy to read and understand.

The goal is to create specifications that can be understood by anyone.  If defining specifications for business experts, the business experts should be able to understand the specifications that you are writing.  The implementation of the specifications in code should be easy to understand by programmers.

Context Awareness

Context is a word used to describe the situations and conditions a system is put into by a user.  Context is often not fresh on the minds of programmers when writing tests because we’re often too caught up in thinking of the technical aspects of the problem.

Focusing on the programming language and the tools around them can distract programmers from context.  Scott Bellware has spoken several times about this concept, and his work is my motivation for this article.

We need to do our best to be aware of context as we write tests, or specifications.  We need to avoid thinking like programmers when writing and programming specifications.  Context Specification can help us with this thinking.

The Context Specification Mindset

Scott Bellware describes the proper Context Specification mindset to have by stating, “Specify the experience, not the implementation.”

Instead of thinking about technical terms and programming ideas like classes, methods, and modules when writing test, consider the subject of the test in a particular context that has a related set of side-effects that you can observe.  The observable side-effects are called specifications in Context Specification grammar.

The test suite isn’t really organized around specific abstractions in your system, unless the abstractions are organized around system behavior instead of data.  Subjects and context can describe one class or several classes.  Specifications reflect the observable effects of one method or several methods.

Subjects, contexts, and specifications show the true value of the code you are creating from the perspective of business objectives rather than implementation details.  If you write specifications for subjects within a context, you end up writing code for what is truly important within your domain.

Example Context Specification Thought Process

There are a number of frameworks and methods to practice Context Specification with. In the .NET world, SpecUnit.NET, MSpec, Specter, or simply NUnit with some syntactic sugar can be used to write specifications.

While these are very functional frameworks, I believe the Ruby RSpec framework is the most elegant and easiest to use. Therefore, to make things simpler I will be writing all of my specifications using RSpec and all of my production code in Ruby.

(I intend to write an article on how to use IronRuby with RSpec to test .NET code; stay tuned)

Let’s use an iPod as an example since most people are familiar with how iPods function at a basic level.  While we write specifications we will remain aware of the context by not performing a technical dissection of the problem.

Context Specification grammar requires a subject, context, and observable conditions.  In this example, the subject is an iPod.  One of the contexts could be, “when playing.”

What conditions can you observe when an iPod is playing?  Without Context Specification, the answer depends on who is answering the question.  If a programmer is answering the question, you will get a very technical answer.  If a layman is answering the question, you will get a more general answer.

The laymen might say, “when an iPod is playing, it should display the track name, artist, and album name; it should also play the track.”

Conversely,  a programmer might say, “when an iPod is playing, it should find the audio file in the audio file database, load it into the audio player, and send a signal to the audio player to start playing the audio track, update the LCD to re-draw the screen with the appropriate information,” and this can go further into more technical dissection.  The programmer’s dialog is hyper-descriptive.

However, with Context Specification, the specifications should be defined in a way so that almost anyone can understand them.  Because the layman understands the context in a more general circumstance, their specifications are more acceptable.

Typically, the best person to work with when defining specifications is the person who knows the specifics of how a system should behave, without necessarily knowing how the technical aspects should be woven together.  A product owner is a good example of this.

Product owners know how their product helps their business, and how it may help their customers.  However, they really don’t care about implementation details, such as the usage of a producer-consumer pattern to manage I/O requests, or adhering to SOLID principles.

Defining a Specification

Let’s start writing specifications for when an iPod is playing.  One of the more obvious specifications, as stated from the laymen’s quote above, is “it should display the track name.”

Using RSpec, here is the code that defines this specification:

  1: require 'spec'
  2: 
  3: describe "iPod", "when playing" do
  4:   it "should display the track name"
  5: end

 

Now let’s look at the RSpec output from this specification:

*
Pending:
iPod when playing should display the track name (Not Yet Implemented)
./ipod_spec_definition.rb:4
Finished in 0.015 seconds
1 example, 0 failures, 1 pending

 

RSpec informs us that there is 1 example and 1 pending, because we haven’t written the implementation of the specification yet.  Notice the readable output?  RSpec can generate a nifty HTML document with all of the specifications for reference.  This can be helpful to business experts for verifying the correctness of specifications.

It is important to note that the context is typically in the gerund form of a verb (“ing” form, e.g “when running,” “when drinking,” etc.)  This eliminates accidental communication of timing or synchronization factors to the reader.

For example, the context “when beginning to run” is irrelevant because it is inclusive in the context “when running.”  The same is true for “when finished running” because that statement occurs “when running” as well.  Programmers might not immediately understand this logic, but from a communication mindset and for the purposes of documentation it conveys the appropriate intent.

Implementing a Specification

Let’s start implementing the first specification:  “iPod when playing should display the track name.”

Following traditional TDD practice, the implementation starts by building a skeleton of the code required for the compiler and with a failing test:

  1: require 'spec'
  2: require 'not_a_mock'
  3: 
  4: class Ipod
  5:   def play
  6:   end
  7: 
  8:   def display_track_name
  9:   end
 10: end
 11: 
 12: describe Ipod, "when playing" do
 13:   before :each do
 14:     @ipod = Ipod.new
 15:     @ipod.track_methods
 16:     @ipod.play
 17:   end
 18: 
 19:   it "should display the track name" do
 20:     @ipod.should have_received(:display_track_name)
 21:   end
 22: end

 

The implementation of the first specification is performed by providing a body to the specification we are trying to implement, asserting that the iPod should received the display_track_name method.

Since the instance variable “ipod” does not exist, the “before” block is added to create a new instance of an “Ipod” class and assigns it to the instance variable needed.  The “before” block is run before each specification is run.

Since all specifications will require an instance of an Ipod, this block makes writing future specifications convenient and further clarifies what subject the specifications are being written for (or more traditionally, the “system under test”).

In order for the Ruby compiler to accept this code, we have to provide a skeleton Ipod class and define the methods that are being called within the specification and the “before” block (“play” and “display_track_name”).

Note that I’ve elected to use a mocking framework called NotAMock.  NotAMock is an extension to RSpec that records all calls on an object in question.  I’ve further extended the framework so that you can call “track_methods” without specifying arguments to the method.  Previously, you would have to specify each method you wanted to track.  My change has been merged to the trunk and can be found here.

After running the new changes in the RSpec runner, the output is:

F
1)
'Ipod when playing should display the track name' FAILED
#<Ipod:0x2b90c24> didn't receive display_track_name
./ipod_spec_implementation_failing.rb:20:
Finished in 0.094 seconds
1 example, 1 failure

 

Notice the test failed with the message “#<Ipod:0x2b90c24> didn't receive display_track_name".  RSpec is reporting that the “display_track_name” method on the instance of the Ipod class was never called.

Now it’s time to execute the next traditional step of TDD:  make the test pass with the least amount of work.  Adding a call to the display_track_name method inside the play method satisfies the RSpec test.  Re-running the RSpec runner will show that the test indeed passes without error.

Now, obviously simply adding a call to the display_track_name method within the play method is an incorrect implementation.  Therefore, we are lead to the final phase of traditional TDD:  refactor:

  1: require 'spec'
  2: require 'not_a_mock'
  3: 
  4: class TrackRepository
  5:   def self.get_random_track_set
  6:     # query database to return random track set
  7:     [ "track1", "track2", "track3" ]
  8:   end
  9: end
 10: 
 11: class Lcd
 12:   def display_track_name(track_name)
 13:     # interface with hardware to display track name
 14:   end
 15: end
 16: 
 17: class Playset
 18:   attr_reader :current_track_name
 19: 
 20:   def self.create_random_playset
 21:     tracks = TrackRepository.get_random_track_set
 22:     playset = Playset.new(tracks)
 23:   end
 24: 
 25:   def initialize(tracks)
 26:     @tracks = tracks
 27:     @current_track_name = @tracks[0]
 28:   end
 29: end
 30: 
 31: class Ipod
 32:   def initialize
 33:     @lcd = Lcd.new
 34:     @current_play_set = Playset.create_random_playset
 35:   end
 36:   
 37:   def play
 38:     display_track_name(@current_play_set.current_track_name)
 39:   end
 40: 
 41:   def display_track_name(track_name)
 42:     @lcd.display_track_name(track_name)
 43:   end
 44: end
 45: 
 46: describe Ipod, "when playing" do
 47:   before :each do
 48:     @ipod = Ipod.new
 49:     @ipod.track_methods
 50:     @ipod.play
 51:   end
 52: 
 53:   it "should display the track name" do
 54:     @ipod.should have_received(:display_track_name)
 55:   end
 56: end

 

The “play” method now asks the “current_play_set” instance variable what the current track name is and provides it to the “display_track_name” method.  This leads to creating the “current_play_set” instance variable, the “Playset” class, and a few other classes and methods.

After running the RSpec test runner again, the above code satisfies the test without issues.  We know that we’ve safely refactored our code to a decent implementation without changing any behavior.

It’s important to realize that one specification could drive out several methods and several classes.  It’s not necessary to pair subjects to classes, and specifications to methods.  Notice the specification we implemented lead to the creation of 4 classes and several methods.

Once further specifications have been introduced at a more detailed level, it’s quite possible to switch contexts or subjects (or both) and further define specifications.  However, this is not a license to start specifying technical terms in our native programming mindset.

Technical specifications should be reworded in less technical terms as much as possible.  Nonetheless, it’s still possible to run into scenarios where technical speech is unavoidable such as in describing state machines.

Summary

Context Specification’s focus is not about providing a new tool to write tests in an innovative way.  Programmers obsess over tools, and often the goal of Context Specification is eclipsed by programmer mentality to find new ways of doing things.

While it does provide a much different way of writing tests, it requires a change in traditional programmer thinking.  A subject within a context must be thought of, and observable conditions form the specifications for that subject in its context.

This demands programmers sway their technical minds from more of a usability, high-level, business oriented mind while defining specifications, and back down to the essence of the technology they are using when writing the code that satisfies those specifications.

Providing documentation this way ensures that whomever can understand the subject and its context can understand the documentation and how that subject is used.  This swaying prevents writing documentation that is too technical.

There are few instances when technical specifications need to be defined for a particular subject or context.  However, one example where I’ve run into that particular situation involves a sub-system that performs connection caching.  It is perfectly acceptable to write specifications for the caching operations under these circumstances because they define an inherent requirement.

Context Specification allows you to write documentation for subjects within specific contexts that are most important, and makes you more aware of what you are trying to test and write code for.

Domain awareness is strengthened by thinking more about a subject and context and less about object-oriented technical feats and the latest and greatest programming technology of the hour.  The quality of a solution’s design and code is improved by getting closer to the true problems in the domain.

I hope you have fun writing specifications for your subjects and their contexts!

Special thanks to Scott Bellware for providing the inspiration of these article and for reviewing it.

An Interesting Article on DDD & Ruby

Fábio Rehm and I have recently started talking about DDD and how to best implement some of the ideas in Ruby.  He just posted a very interesting article on his attempt at implementing a basic customer/order DDD example in Ruby.  Check it out:  http://dddonruby.wordpress.com/2009/06/08/my-aproach-for-doing-ddd-on-ruby-introduction-and-part-i

Expressiveness in Modern Programming Languages

iStock_000009524688XSmall The Technical Aptitude Required for Object-Oriented Programming

One of the primary purposes of an object-oriented programming language is to recreate representations of solutions to specific problems with code.  These representations are transformations of solutions that were typically solved previously with human interactions.

Sometimes certain problems are solved only with the feat of modern computer software.  In other facets, ingenuous minds forge an inventive technology in software not unlike the ways of the great mechanical inventors of our age.

Traditional object-oriented programming has been one of those great technical inventions.  While simple guidelines allow most programmers to attempt object-oriented programming, only few understand the principles and practices to actually model real concepts correctly.

Frequently, programmers create objects that simply do not exist in the domain, or are bastardizations of the true domain concept.  Unfortunately, in these scenarios success is short-lived.  Maintainability quickly degrades, and often the code is completely thrown away.

Incorrect representations of the domain are similar to the adage of trying to fit a square peg into a round hole.  Perhaps it’s possible to do this with creative technical feats.  However, as more features are requested, as bugs are fixed, and the code evolves, misrepresentations are quickly revealed.

When this happens, feature requests and bug fixes take longer than expected, and a tremendous amount of pressure is put on the development team to meet deadlines.  Managers and executives begin to wonder why it is taking so much time to complete what they think are smaller tasks.

All-nighters, pizza, and an insane amount of coffee support the developers through their death march.  If you have been developing for a decent amount of time, I am sure you have told of such horror stories over lunch or over a good beer.

The Expressiveness of a Language Matters

How do we prevent this pain?  There are several factors that contribute to these unfortunate events, however expressiveness within source code can be an important piece of the puzzle.  The best developers are often the most expressive in the language he or she is operating in.

Instead of engineering solutions using technical wizardry and concepts that only programmers can understand, being expressive within the source code can keep business rules intact as development continues and solutions evolve.

Expressiveness is a quality that developers must constantly be working toward.  Interestingly enough, the choice of a programming language can greatly assist in this struggle.  The more expressive a language is, the easier it is for the programmer to think about the domain.

If non-programmer types and developers communicate with a ubiquitous language, this language can pervade into the source code.  The pervasiveness of a domain’s language allows ideas to be more easily understood.

Contrasting Language Expressiveness

On May 30th, 2009, Scott Bellware gave a talk entitled, “A Handful of Things You Can Do in Ruby That Scares the Pants Off of C# Developers.”  In this fascinating talk, Scott showed two functionally equivalent, plain classes:  one defined in C#, the other defined in Ruby.

An interesting illustration emerged when Scott showed how the C# class’s definition comprised of a lot more statements to instruct the compiler on what to do and how to perform.  These statements take the programmer’s mind away from the domain and bring him or her to thinking in a technical programming wonderland.

On the other hand, the Ruby class contained very little compiler instructions; the class was mostly comprised of domain expressions.  This is a powerful illustration, so I’d like to show it as well as a means of illustrating expressiveness.

Let us define a simple class that records recommendations of businesses.  Recommendations can be used for any business type, such as restaurants.  A ‘recommender’ can recommend a business only once.  The class will allow a means to determine how many recommendations all of the businesses have received.

The C++ Definition (brace yourself)

First, I will define the class using one of the most traditional object-oriented programming languages:  C++.  Let’s start as we always do with C++:  with a header file definition:

   1: #pragma once
   2: #include <map>
   3: #include <vector>
   4: #include <string>
   5:  
   6: using namespace std;
   7:  
   8: namespace BusinessRecommendations
   9: {
  10:     class RecommendationTracker
  11:     {
  12:     public:
  13:         typedef vector<pair<string, unsigned int>> RecommendationTotalsList;
  14:  
  15:         void AddRecommendation(const char* recommender,
  16:                                const char* business);
  17:         void GetRecommendationTotals(RecommendationTotalsList& recommendations) const;
  18:  
  19:     private:
  20:         typedef map<string, vector<string>> RecommendationsMap;
  21:  
  22:         RecommendationsMap recommendations;
  23:  
  24:         bool HasRecommenderRecommendedBusiness(const char* recommender,
  25:                                                const char* business);
  26:         void RecordRecommendation(const char* business, const char* recommender);
  27:     };
  28: }

This will define the basic contract that consuming code will use to work with the RecommendationTracker class.  Pay particular attention to the usage of pointers, references, “constness”, and other statements used to tell the compiler what to do.

Now that we’ve declared the contract, lets look at the overly verbose definition of the class:

   1: #include <algorithm>
   2: #include "RecommendationTracker.h"
   3:  
   4: using namespace std;
   5:  
   6: namespace BusinessRecommendations
   7: {
   8:     void RecommendationTracker::AddRecommendation(const char* recommender,
   9:                                                   const char* business)
  10:     {
  11:         if (HasRecommenderRecommendedBusiness(recommender, business))
  12:             throw exception("You may only recommend the same business once.");
  13:  
  14:         RecordRecommendation(business, recommender);
  15:     }
  16:  
  17:     void RecommendationTracker::GetRecommendationTotals(
  18:         RecommendationTotalsList& recommendationTotals) const
  19:     {
  20:         for (RecommendationsMap::const_iterator it =
  21:             recommendations.begin();
  22:             it != recommendations.end();
  23:             ++it)
  24:         {
  25:             recommendationTotals.push_back(make_pair(it->first,
  26:                 it->second.size()));
  27:         }
  28:     }
  29:  
  30:     bool RecommendationTracker::HasRecommenderRecommendedBusiness(
  31:         const char* recommender,
  32:         const char* business)
  33:     {
  34:         RecommendationsMap::const_iterator it =
  35:             recommendations.find(business);
  36:         if (it == recommendations.end())
  37:             return false;
  38:  
  39:         return find(it->second.begin(), it->second.end(), recommender) !=
  40:             it->second.end();
  41:     }
  42:  
  43:     void RecommendationTracker::RecordRecommendation(const char* business,
  44:                                                      const char* recommender)
  45:     {
  46:         recommendations[business].push_back(recommender);
  47:     }
  48:  }

Once again, we have several statements regarding constant state of pointers, language like “it->second.size()”, which refers to the size of a vector that is the value portion of a map.  Look at how the class name is repeated before each function definition; this tells the compiler what scope we are in.

Although not appearing in this example, frequently C++ programmers have to tell the compiler whether they want to allocate objects’ memory on the stack or heap, and have to manage the lifetime of that memory.

All of these intrinsic C++ concepts force the programmer to be very technical.  Much time is spent on considering the language itself rather than the domain.

The C# Definition (a little better)

Now lets port this same class to C#.  C# is a bit more modern than C++, slightly more functional, and isn’t so concerned about telling the compiler what to do.

One slight problem:  since there is no easy way to return a list of pairs as in the C++ definition, I’ve created a small helper class called “RecommendationTotal.”  I could have returned a dictionary, but the usage of a dictionary in C# usually suggests something other than name/value pairs:

   1: namespace BusinessRecommendations
   2: {
   3:     public class RecommendationTotal
   4:     {
   5:         public RecommendationTotal(string business, uint numberOfRecommendations)
   6:         {
   7:             Business = business;
   8:             NumberOfRecommendations = numberOfRecommendations;
   9:         }
  10:  
  11:         public string Business
  12:         {
  13:             get;
  14:             private set;
  15:         }
  16:  
  17:         public uint NumberOfRecommendations
  18:         {
  19:             get;
  20:             private set;
  21:         }
  22:     }
  23: }

Some may argue that this adds readability to the code; for C#, it probably does.  Now, the definition of the RecommendationTracker class is as follows:

   1: using System;
   2: using System.Collections.Generic;
   3:  
   4: namespace BusinessRecommendations
   5: {
   6:     public class RecommendationTracker
   7:     {
   8:         private readonly IDictionary<string, ICollection<string>>
   9:             recommendations = new Dictionary<string, ICollection<string>>();
  10:  
  11:         public void AddRecommendation(string recommender, string business)
  12:         {
  13:             if (HasRecommenderRecommendedBusiness(recommender, business))
  14:                 throw new Exception(
  15:                 "You may only recommend the same business once.");
  16:  
  17:             RecordRecommendation(business, recommender);
  18:         }
  19:  
  20:         public IEnumerable<RecommendationTotal> RecommendationTotals
  21:         {
  22:             get
  23:             {
  24:                 foreach (var business in recommendations.Keys)
  25:                 {
  26:                     yield return new RecommendationTotal(business,
  27:                         (uint) recommendations[business].Count);
  28:                 }
  29:             }
  30:         }
  31:  
  32:         private bool HasRecommenderRecommendedBusiness(
  33:             string recommender, string business)
  34:         {
  35:             return recommendations.ContainsKey(business) &&
  36:                 recommendations[business].Contains(recommender);
  37:         }
  38:  
  39:         private void RecordRecommendation(string business,
  40:                                           string recommender)
  41:         {
  42:             if (!recommendations.ContainsKey(business))
  43:             {
  44:                 recommendations[business] = new List<string>();
  45:             }
  46:  
  47:             recommendations[business].Add(recommender);
  48:         }
  49:     }
  50: }

It’s nice that the declaration and definition are now combined into one, and we don’t have to deal with header files anymore.  However, there are still many references to compiler instructions.

Every method must tell the compiler whether it should make the method public or private.  There are several references to types, such as “IEnumerable<RecommendationTotal>” and “IDictionary<string, ICollection<string>>.”

These compiler instructions and type declarations take our minds away from thinking about the business and have us spend more time on thinking about programming.

The Ruby Definition

Now let’s look at the same code in Ruby:

  1: class RecommendationTracker
  2:     def add_recommendation(recommender, business)
  3:       has_recommender_recommended_business?(recommender, business) do
  4:         raise StandardError, 'You may only recommend the same business once.'
  5:       end
  6: 
  7:       @recommendations[business] << recommender
  8:     end
  9: 
 10:     def recommendation_totals
 11:       recommendation_totals = Hash.new
 12: 
 13:       @recommendations.each_key do |business|
 14:         recommendation_totals[business] =  @recommendations[business].length
 15:       end
 16: 
 17:       recommendation_totals
 18:     end
 19: 
 20:   private
 21:     def initialize
 22:       @recommendations = Hash.new do |recommendations, business|
 23:         recommendations[business] = Array.new
 24:       end
 25:     end
 26: 
 27:     def has_recommender_recommended_business?(recommender, business)
 28:       return false if !@recommendations.has_key? business
 29:       return false if !@recommendations[business].include?(recommender)
 30: 
 31:       yield
 32:       true
 33:     end
 34: end

Notice how concise the Ruby definition is?  Compiler instructions are kept to a minimum, and the statements seems to emphasize the logic of the domain more.  Furthermore, all unnecessary references to types have been removed.

Simple one line boolean statements have been rearranged in a “action if condition” format instead of “if action then condition.”  Methods have a question mark to add to readability.  Overall, the code is simply a lot more expressive.

Summary

Contrast the Ruby definition to the original C++ definition.  We’ve come a long way haven’t we?  What’s frightening is to think that Ruby was created in 1992!  For some reason, it’s only recent that it has been receiving a lot of attention (probably due to Rails and the direction of software toward web platforms).

C++ is still a great language, and has its place within specific industries.  Game development is still primarily performed in C++, mainly due to the performance requirements where every bit of speed must be squeezed out of the code.

C++ is definitely a professional programming language, and requires the programmer to understand the power it can provide.  A high degree of technical aptitude is required to create concrete, easy to understand, scalable, and powerful applications in C++.  Unfortunately, it also takes more time.

However, instead of focusing on technical aptitude, developers should focus on adding value to the domain they are developing for.  This means most thought should be spent thinking about how best to express the core domain, where all of the business rules and logic should reside.

Compiler instructions and nifty technical tricks are cool, but they take time away from adding to the core domain.  As Eric Evans has illustrated, the easiest way to determine the core domain is ask why is the development effort important?  How will the code you are writing help the business compete?

The answer to those questions should point you straight to the core domain.  Expressiveness allows developers to define the core domain with a refined eloquence that helps lead to a deeper understanding of the problems at hand.

If source code is less infected with technical concepts, true domain problems are more easily read.  This greatly adds to maintainability, and allows object-oriented principles to be practiced more easily.

I am excited to see more developers diving into Ruby.  Hopefully, as the community becomes even more established than it already is, we will all start creating more elegant designs and expressions of problems without focusing too much on telling the compiler what to do.