<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>8th Light Blog</title>
 <link href="http://8thlight.github.com/atom.xml" rel="self"/>
 <link href="http://8thlight.github.com/"/>
 <icon>http://8thlight.github.com/favicon.ico</icon>
 <updated>2013-05-15T09:25:09-07:00</updated>
 <id>http://8thlight.github.com/</id>

 
 <entry>
   <title>What is a Bug?</title>
   <category term="eric-smith" />
   <link href="http://8thlight.github.com/eric-smith/2013/05/13/what-is-a-bug.html"/>
   <author>
     <name>eric smith</name>
   </author>
   <updated>13 May 2013</updated>
   <id>http://8thlight.github.com/eric-smith/2013/05/13/what-is-a-bug</id>
   <content type="html">&lt;p&gt;A while back I wrote a little about &lt;a href=&quot;http://blog.8thlight.com/eric-smith/2013/04/08/we-are-principled-6&quot;&gt;bugs&lt;/a&gt;. I got a few questions and objections and the most common one goes something like this:&lt;/p&gt;

&lt;p&gt;&quot;Fixing all the bugs is silly. You're telling me that when somebody reports that IE4 and Netscape Navigator don't properly render your CSS3 transitions, you go in and fix it? Many bugs aren't worth the cost to fix them.&quot;&lt;/p&gt;

&lt;p&gt;I'm exaggerating, but only slightly. Generally people point out obscure miniscule errors as a way to say my argument is invalid. My answer to the above is simple: that's not a bug. Nobody would reasonably expect that scenario to work, and if it is really necessary it's a user story and should be treated as such. The follow up question to this is naturally...&lt;/p&gt;

&lt;h2&gt;What Is A Bug?&lt;/h2&gt;

&lt;p&gt;My comprehensive definition of a a bug is:&lt;/p&gt;

&lt;p&gt;  &lt;em&gt;A defect is a part of the system that does not work as specified, or a part of the system that while unspecified behaves egregiously (i.e. it crashes).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That sounds professional enough but doesn't really help the situation.  I prefer this definition:&lt;/p&gt;

&lt;p&gt;  &lt;em&gt;A defect is when a developer makes a mistake.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's a lot better, even though it's less comprehensive and would make a lawyer puke. The important terms to remember here are &quot;developer&quot; and &quot;mistake.&quot; Business Analyst specifies the wrong feature? Write a new User Story to change the feature. Nobody likes the new feature? New User Story. If the system doesn't work on IE4 and it needs to, well then you should probably write a story. Let's go through a few examples of potential bug reports, and see if they are really bugs or not.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;When I generate a PDF it takes 40 seconds to generate. Fix.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When it comes to the bug or story question, performance and scaling is one of the trickiest areas to make that decision. When determining whether or not a performance issue is a defect you have to ask a few questions:&lt;/p&gt;

&lt;h3&gt;Was a minimum performance specified?&lt;/h3&gt;

&lt;p&gt;If we have a system that generates PDFs, like the bug report above, did we originally specify a maximum time limit? If we did then this is probably a bug. Even then we may need to evaluate if the time limit is still reasonable, as we may have added a lot of features to the PDF generation causing it to slow down. We may even be able to throw hardware at it. Still, if we specified a minimum performance and are not meeting it, then it's probably a bug.&lt;/p&gt;

&lt;h3&gt;Is the performance embarassingly slow?&lt;/h3&gt;

&lt;p&gt;I keep using emotional terms instead of precise ones, because I usually know in my gut when I've screwed up. Imagine you changed the PDF system above and instead of the bug report saying 40 seconds, it said 2 minutes, or 2 hours. At some point it becomes clear, regardless of specification, that the system is way too slow. Think of links that take 10 seconds, video games that run at 4 frames a second, or iPhone apps that take 2 minutes to start. Context matters, but we don't want to force our customer to write on each feature &quot;and it should be fast enough that the user doesn't think it crashed.&quot; Obvious bugs are obvious.&lt;/p&gt;

&lt;h3&gt;Did it drastically regress?&lt;/h3&gt;

&lt;p&gt;If the performance was unspecified and is within reasonable parameters, then did it suddenly and drastically regress? Forty seconds may have seemed fine in isolation, but not if it used to take 3. If it did, then we need to look into the reasons why. Perhaps this slowdown was expected, or perhaps it was an accident. Did you add new features and cause a natural slow down? Or did you screw up? That's the difference between a feature and a bug.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;On Mobile Safari the popup dialogs don't work&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a variation on the IE4 bug I described earlier, but one that somebody might reasonably report. The first question to ask is, &quot;Was it specified?&quot; If not, then we ask &quot;Is it reasonable for it to work?&quot; For instance many of the JQuery-UI widgets don't work on Mobile Safari. If you've got an application that's existed for a long time and has many of these dialogs, then it might be a series of stories to optimize the behavior on mobile browsers.&lt;/p&gt;

&lt;p&gt;On the other hand if you started the application three months ago and it's been made clear that mobile browsers are a target platform, then hell yes this is a bug. As the developer you should have ensured your code would work across browsers. You made a mistake.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The New File wizard takes too many clicks to get through.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In an Agile approach we refine our solutions over time. We get the first version of an application shipped quickly and get customer feedback as soon as possible, then use that feedback to improve the product. This is closer to what Ward Cunningham meant when he coined the term &lt;a href=&quot;http://en.wikipedia.org/wiki/Technical_debt&quot;&gt;Technical Debt&lt;/a&gt;. We ship code with limited knowledge, and frequently that limited knowledge manifests itself in phrases like: &quot;Well it does the job, but I don't like it.&quot;&lt;/p&gt;

&lt;p&gt;It is to be expected that no matter how much effort and preparation you put into your user story, even after writing passing acceptance tests, sometimes the completed story will be what the customer said but not what the customer wanted.&lt;/p&gt;

&lt;p&gt;This is not a bug. You're refining an existing story based on new information, and should do so in the context of your existing process. Frequently these types of discussions get contentious because the customer, and even other developers in the room, feel like it's a bug. Under pressure to get the stories done right the first time we forget that iterations provide a mechanism for improving existing features, and refining previously played user stories.&lt;/p&gt;

&lt;p&gt;There are two ways to help defuse this situation. The first is to remind the customer that they can create more User Stories to improve a feature when there isn't pressure. Sometimes a customer will see something demoed and accept it, but you can just tell it's less than thrilling. Start asking questions about what's wrong, encourage them to write more stories. Remind them that software is never really finished.&lt;/p&gt;

&lt;p&gt;The second is to do little things without stories. If the color is wrong, or an icon in the wrong place, just change it right after the meeting. This reminds the customer that your relationship isn't adversarial, and doesn't really hurt you as a developer. They'll see that when you argue against something being a bug you aren't standing on ceremony, but trying build the best software you know how.&lt;/p&gt;

&lt;h3&gt;All bugs must die, all the time&lt;/h3&gt;

&lt;p&gt;Pete McBreen wrote: &quot;A Software Craftsman ships with no known defects.&quot; This scares a lot of developers, who are concerned that they'll spend all their time gold-plating and never ship, but I believe it's a fundamental idea. It is immoral to know something is broken and ship it anyway. That doesn't mean my software is perfect. It means that I do not knowingly cause problems for users.&lt;/p&gt;

&lt;p&gt;We don't accept anything less from other industries. Why should software be any different?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We are Principled: 8th Edition</title>
   <category term="joey-shipley" />
   <link href="http://8thlight.github.com/joey-shipley/2013/04/24/we-are-principled-8.html"/>
   <author>
     <name>joey shipley</name>
   </author>
   <updated>24 Apr 2013</updated>
   <id>http://8thlight.github.com/joey-shipley/2013/04/24/we-are-principled-8</id>
   <content type="html">&lt;h2&gt;Steadily Adding Value&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;
    We estimate with diligence.&lt;br /&gt;
    We do not let fear or pressure make us promise what we can’t deliver.
  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Estimation is Difficult&lt;/h3&gt;

&lt;p&gt;As craftsmen, we pay close attention to the pain points in our environments and processes. To help produce better results, we create a variety of technologies and practices, including programming principles, organizational frameworks, even standing desks and ergonomic chairs. In our industry, when we perceive a pain, we strive to cure it. How then does the practice of estimation fit in with pain points?&lt;/p&gt;

&lt;p&gt;Some people may estimate low for fear of making the client uncomfortable. A low estimate might come from feeling that others would think less of one’s capabilities. Some people may estimate high because the team lead is constantly pushing hard deadlines. Then again, some estimate high from the feeling that they are not as skilled as others. Unprepared estimates bring a high degree of uncertainty, they are no more than predictions of the future, where we will be more wrong than we are right. These assumptions and behaviors, among others, contribute to estimates that lead us down the dangerous path of promising what we can not provide.&lt;/p&gt;

&lt;p&gt;Estimates are ripe with pain. The pain exists for all parties involved.&lt;/p&gt;

&lt;h3&gt;Estimation is Important&lt;/h3&gt;

&lt;p&gt;When one takes on a task and establishes a timeframe for when it will be completed, everyone involved looks at it differently.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Stakeholders look at how much the work will cost them compared to the functionality delivered.&lt;/li&gt;
  &lt;li&gt;Product owners look at the current status of the project and use estimates for prioritization of upcoming work.&lt;/li&gt;
  &lt;li&gt;Teams look at the estimate and uses it for coordinating other work that is needed for the task.&lt;/li&gt;
  &lt;li&gt;Craftsmen look at the estimate as a commitment and a testament of their capabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estimates that over-promise degrades the trust others have in our word. Under-promising is unsatisfying when it is just minimizing the gap. When one loses confidence in oneself, their day to day work life becomes taxed and stressed. When the team lead loses confidence in one’s ability to estimate, their chances for growth and success in the organization suffers. When the client loses confidence, the organization risks losing the client.&lt;/p&gt;

&lt;p&gt;Due to the ownership, pressure, competing tensions, and difficulty, we often become afraid of estimating. We begin to dread estimation, start avoid it as best as we can. We hear others saying “Developer’s are horrible at estimation, you can’t trust what they say about deadlines.” We start saying it ourselves.&lt;/p&gt;

&lt;h3&gt;Principled Estimations&lt;/h3&gt;

&lt;p&gt;At 8th Light we take the challenge of estimations seriously. We know our clients will lose faith if we don’t have strict processes for estimation. As such we take pride in our efforts to improve our estimation processes and practices.&lt;/p&gt;

&lt;p&gt;Our estimation process is a group effort. Several craftsmen contribute what they know about the system under consideration. This brings us to a better understanding of what it will take us to complete the work. We collaborate with our clients on the estimates, so they can see the internal discussion and understanding we have of the work. The timeframe for the estimates is not a hard date, but rather a range of possible outcomes.&lt;/p&gt;

&lt;p&gt;We have adapted an estimation technique called PERT (Program Evaluation and Review Technique) to augment our process. We estimate an optimistic amount of time, a realistic amount of time, and a pessimistic or worse case timeline. From those three estimates we determine how to charge for the work and then make a commitment. Whenever we find that our estimates were either too high or too low, we work through what caused it.&lt;/p&gt;

&lt;h3&gt;Diligence&lt;/h3&gt;

&lt;p&gt;Our diligence in estimation has led to many changes in our process. The methods we use now are not what we used at first, and we will continue to improved them. At the very core of our process is the determination not to let the fears or pains of estimation take over. We will not promise what we cannot deliver, even if that means we have to tell our client “No”. When we as craftsmen make promises we cannot keep. we give others reasons not to trust our abilities.&lt;/p&gt;

&lt;p&gt;Estimating work is important. Estimating should be treated no differently than any of our other practices. Estimates are commitments. They affect our image as professionals. Estimates must be honest, whether we are saying “Yes” or “No”. And above all else, we cannot let our fears allow us to promise what we cannot deliver.&lt;/p&gt;

&lt;h3&gt;More on Estimation&lt;/h3&gt;

&lt;p&gt;
  &lt;a href=&quot;http://blog.8thlight.com/uncle-bob/2012/04/20/Why-Is-Estimating-So-Hard.html&quot; target=&quot;_blank&quot;&gt;Why is Estimating so Hard? by Uncle Bob&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href=&quot;http://blog.8thlight.com/paul-pagel/FromEstimateToCommitment.pdf&quot; target=&quot;_blank&quot;&gt;From Estimate to Commitment, by Paul Pagel&lt;/a&gt;
&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We are Principled: 7th Edition</title>
   <category term="steve-kim" />
   <link href="http://8thlight.github.com/steve-kim/2013/04/16/we-are-principled-7.html"/>
   <author>
     <name>steve kim</name>
   </author>
   <updated>16 Apr 2013</updated>
   <id>http://8thlight.github.com/steve-kim/2013/04/16/we-are-principled-7</id>
   <content type="html">&lt;blockquote&gt;
  &lt;p&gt;
    We work at a sustainable pace.&lt;br /&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;We do not burn out.
    &lt;cite&gt;&amp;mdash;from the 8th Light Principles of Steadily Adding Value&lt;/cite&gt;
  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  At 8th Light, we devote ourselves to crafting durable software for our clients. It is important that we give our full, undivided attention to our work and focus on the needs of the client during the entire life of a project.
&lt;/p&gt;
&lt;h3&gt;
  We work at a sustainable pace
&lt;/h3&gt;
&lt;p&gt;
  Software is a craft of unique problems. As practitioners, we are constantly inventing solutions which require us to tread into the unknown. Constantly operating in this uncertainty is difficult and requires constant engagement to build up the contexts necessary.
&lt;/p&gt;
&lt;p&gt;
  When we expend our attentional resources&lt;sup&gt;&lt;a href=&quot;#footnote_1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; by working long hours, we can no longer think clearly about the problem in front of us. Our brains switch from peripheral to tunnel. We make bad decisions and implement wrong, corner-cutting solutions. By setting a sustainable pace, we commit ourselves to using the hours of the day efficiently and effectively. Working at a pace within our capacity for attention allows us to deliver quality features iteration after iteration.
&lt;/p&gt;
&lt;h3&gt;
  We do not burn out
&lt;/h3&gt;
&lt;p&gt;
  We live by the code of endurance. We do not sprint to the finish line&lt;sup&gt;&lt;a href=&quot;#footnote_2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. We know projects are like marathons. If we overwork ourselves, our minds become fatigued and we write sloppy code. It takes more time and effort the next day to rewrite or refactor the mess we created from the night before.
&lt;/p&gt;
&lt;p&gt;
  I remember being on a project where we were asked to work overtime to meet an impossible deadline. At the time, however, I thought to myself, “OK, this shouldn’t be too bad. It’s only for one month. I can do this.” For the entire month, I put in anywhere from 10 to 16 hours each day. Not surprisingly, we didn’t meet the deadline and I was completely exhausted. The following months, my body and mind were in recovery mode. I was writing terrible code. I skipped refactoring opportunities, skipped on some tests and adopted an I-don’t-care attitude. I put in my regular hours and left for the day not ever thinking about work.
&lt;/p&gt;
&lt;p&gt;
  Marathon runners know the importance of staying within their pace. There is high risk when sprinting at any moment during the race. It can jeopardize results and do bodily harm. In much the same way, we understand sprinting can produce negative effects. It’s a short-term reward with long-term negative consequences. Slow and steady wins the race.
&lt;/p&gt;
&lt;p&gt;
  We deliver value to our clients while maintaining a 36 hour work week. We set aside Friday afternoons for fun, open source projects. This gives us an opportunity to learn something new outside of client projects. We also get to pair with others that we do not normally pair with.
&lt;/p&gt;
&lt;p&gt;
  This is not to say we are perfect. There are situations where we need to work on Friday afternoons, even nights and weekends. But this is not the norm. We strive to maintain a sustainable pace.
&lt;/p&gt;
&lt;p&gt;
  As craftsmen, we uphold our belief that overworking leads to bad code and wasted time. We make the most of our eight hour burns&lt;sup&gt;&lt;a href=&quot;#footnote_3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. We are modern-day endurance runners like Pheidippides. We’re in it to succeed. But we realize that to succeed, we must craft our code at a sustainable pace.
&lt;/p&gt;
&lt;h5&gt;&lt;strong&gt;We at 8th Light are principled; and this is one of the principles we follow.&lt;/strong&gt;&lt;/h5&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a name=&quot;footnote_1&quot;&gt;&lt;/a&gt;
  [1] From the book, &lt;a href=&quot;http://pragprog.com/book/ahptl/pragmatic-thinking-and-learning&quot;&gt;Pragmatic Thinking and Learning: Refactor Your Wetware&lt;/a&gt;. We're not out of time, but out of attention.
&lt;/p&gt;
&lt;p&gt;
  &lt;a name=&quot;footnote_2&quot;&gt;&lt;/a&gt;
  [2] Scrum uses the word &quot;sprint&quot; to represent duration. But how can we keep sprinting all the time, week after week? We like to use the term “iteration” instead of “sprint” for this very reason.
&lt;/p&gt;
&lt;p&gt;
  &lt;a name=&quot;footnote_3&quot;&gt;&lt;/a&gt;
  [3] Uncle Bob renamed Extreme Programming's 40-hour week to 8-hour burn.
&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>When is a character not a Character?</title>
   <category term="colin-jones" />
   <link href="http://8thlight.github.com/colin-jones/2013/04/11/when-is-a-character-not-a-character.html"/>
   <author>
     <name>colin jones</name>
   </author>
   <updated>11 Apr 2013</updated>
   <id>http://8thlight.github.com/colin-jones/2013/04/11/when-is-a-character-not-a-character</id>
   <content type="html">&lt;p&gt;Characters seem like an easy concept to grasp at first glance. Java has a
primitive type &lt;code&gt;char&lt;/code&gt; and an object wrapper &lt;code&gt;Character&lt;/code&gt;, and these are pretty
close to what we mean when we talk about characters. However, these data types
aren't fully inclusive of the set of characters we might actually encounter in
programs. There are good historical / compatibility reasons for this
inconsistency, detailed in the &lt;a href=&quot;http://docs.oracle.com/javase/7/docs/api/java/lang/Character.html&quot;&gt;Character
javadocs&lt;/a&gt;,
but I'd like to share a debugging story to highlight the difference between the
Java types and what we usually mean when we say &quot;character&quot;.&lt;/p&gt;

&lt;p&gt;Recently I received a bug report for
&lt;a href=&quot;https://github.com/trptcolin/reply&quot;&gt;REPLy&lt;/a&gt;, where the character represented by
&lt;code&gt;&quot;\ud800\udf30&quot;&lt;/code&gt; &lt;sup&gt;&lt;a href=&quot;#footnote_1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, when pasted as
input, would immediately exit the program (a nasty bug, to be sure). After a
bit of yak shaving, &lt;a href=&quot;https://twitter.com/li_hsuan&quot;&gt;Li-Hsuan&lt;/a&gt; and I traced the
problem down to a class in &lt;a href=&quot;https://github.com/jline/jline2&quot;&gt;Jline&lt;/a&gt; responsible
for converting bytes from an input stream into characters. Java has a class
&lt;code&gt;java.io.InputStreamReader&lt;/code&gt; that's responsible for doing exactly this, but it
wasn't being used, possibly for historical reasons. The class had been taken
from the &lt;a href=&quot;http://harmony.apache.org/&quot;&gt;Apache Harmony&lt;/a&gt; codebase, and it
correctly handled many non-ASCII UTF-8 characters. All of the characters that
worked, however, were a single char in length. The broken ones were non-BMP
characters: this was the crux of the problem.&lt;/p&gt;

&lt;p&gt;BMP here isn't &lt;a href=&quot;http://en.wikipedia.org/wiki/BMP_file_format&quot;&gt;the image file
format&lt;/a&gt;, as I learned, but rather
the &lt;a href=&quot;http://en.wikipedia.org/wiki/Plane_%28Unicode%29#Basic_Multilingual_Plane&quot;&gt;Basic Multilingual
Plane&lt;/a&gt;.
The BMP characters are up to 2 bytes in length (values &lt;code&gt;0x0000&lt;/code&gt;-&lt;code&gt;0xffff&lt;/code&gt;), so
there's a good bit of coverage there. Characters from the Chinese, Thai, even
Mongolian alphabets are there, so if you're not an encoding expert, you might
be forgiven if your code only handles BMP characters. But all the same,
characters like &lt;a href=&quot;http://www.fileformat.info/info/unicode/char/10330/index.htm&quot;&gt;the one in
question&lt;/a&gt; won't
be correctly handled by code that assumes it'll fit into two bytes.&lt;/p&gt;

&lt;p&gt;Back to Java: a &lt;code&gt;char&lt;/code&gt; is &lt;a href=&quot;http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.1&quot;&gt;defined to hold 16 bits of
data&lt;/a&gt;,
and the &lt;code&gt;Character&lt;/code&gt; type is just a wrapper around that. And as we've just
learned, that's precisely enough space to hold the full range of BMP
characters. But when it comes to non-BMP characters, which have values greater
than &lt;code&gt;0xffff&lt;/code&gt;, we're out of luck.&lt;/p&gt;

&lt;p&gt;The right approach is to bridge the language-level chars to semantic characters
by using an API like &lt;a href=&quot;http://docs.oracle.com/javase/7/docs/api/java/lang/Character.html#codePointAt%28char[],%20int%29&quot;&gt;&lt;code&gt;Character.codePointAt(char[], int)&lt;/code&gt;&lt;/a&gt;
to get one real character starting at a given index in the input array.
The output is an &lt;code&gt;int&lt;/code&gt;, which is what we need to have enough room for these
non-BMP characters. Impressively, the class with the bug was already using an array
of chars; it just turned out to only be using one element (of a 1-char array) instead
of allowing for multi-char characters. This ended up causing an error case
where the alternate &lt;code&gt;InputStreamReader&lt;/code&gt; would return -1 to indicate the end of
the stream. &lt;a href=&quot;https://github.com/jline/jline2/pull/80&quot;&gt;The actual fix&lt;/a&gt; was easy,
but as usual, understanding the context surrounding the issue was less so.&lt;/p&gt;

&lt;p&gt;As I write this, there are currently 417,000 Google results for &lt;code&gt;non-bmp
character bug&lt;/code&gt;. This kind of confusion is definitely not limited to Java or
even the JVM. If you're interested in learning more about how Unicode works,
&lt;a href=&quot;http://en.wikipedia.org/wiki/Mapping_of_Unicode_character_planes&quot;&gt;the wikipedia article on Unicode character
planes&lt;/a&gt; is as
good a place to start as any. There's much more complexity here than I've
described in this short post, but if this prevents at least one future mistaken
assumption about the size of a character, I'm happy.&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;footnote_1&quot;&gt;&lt;/a&gt;
[1] I'll settle for &lt;a href=&quot;http://www.fileformat.info/info/unicode/char/10330/index.htm&quot;&gt;linking the description
here&lt;/a&gt; since
something in my toolchain, likely either my browser or my font set, doesn't
support it within this blog post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We are Principled: 6th Edition</title>
   <category term="eric-smith" />
   <link href="http://8thlight.github.com/eric-smith/2013/04/08/we-are-principled-6.html"/>
   <author>
     <name>eric smith</name>
   </author>
   <updated>08 Apr 2013</updated>
   <id>http://8thlight.github.com/eric-smith/2013/04/08/we-are-principled-6</id>
   <content type="html">&lt;blockquote&gt;
  &lt;p&gt;
  We take responsibility for the correctness of our code by testing it thoroughly.&lt;br/&gt;
  &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;We do not tolerate preventable defects.
  &lt;cite&gt;&amp;mdash;from the 8th Light Principles of Well-Crafted Software&lt;/cite&gt;
  &lt;/p&gt;
&lt;/blockquote&gt;


&lt;p&gt;Bugs. Defects. Errors, whatever you want to call them they are the scourge of our industry, and they will continue to be so until we take them seriously.&lt;/p&gt;

&lt;p&gt;&quot;But Eric!&quot; you say, &quot;We already take our defects seriously! We categorize them, prioritize them, describe them. We have a huuuge database and a gigantic QA department. How can you say we don't take our defects seriously?&quot;&lt;/p&gt;

&lt;p&gt;Simple: &lt;em&gt;If you took your defects seriously you would fix them!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Think of all the things I just mentioned. Huge QA departments running manual test scripts, bug tracking databases, bug prioritization meetings, etc. all exist because as an industry we have accepted the premise that software will have a huge number of defects. So many that we can't possibly fix all of them. Instead we track them, and fix only the &quot;important&quot; ones.&lt;/p&gt;

&lt;p&gt;At 8th Light we reject this premise. It is not that we believe we don't make mistakes, but we work as hard as we can to prevent them and take responsibility for fixing them.&lt;/p&gt;

&lt;h2&gt;We Test Thoroughly&lt;/h2&gt;

&lt;p&gt;We practice Test Driven Development and it's not negotiable. If you want to &quot;save time&quot; by eliminating this, you'll get a straightforward answer.&lt;/p&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;Any 8th Light team will immediately begin writing tests first, and will write tests for any untested code they encounter. But TDD alone will not produce a quality product.&lt;/p&gt;

&lt;p&gt;Each team will add higher-level testing suitable to the context of the project. We may use Fitnesse, Cucumber, Selenium and others. We'll write integration tests as necessary. When we find a defect we will write a test to make sure it never happens again.&lt;/p&gt;

&lt;p&gt;Finally we will manually test, and in a special way. In the &lt;a href=&quot;http://blog.8thlight.com/paul-pagel/2013/03/12/3rd-edition.html&quot;&gt;third edition&lt;/a&gt; of this series Paul detailed our demo process. He mentioned that the demo is &lt;em&gt;practiced&lt;/em&gt;, which is something we developed on one of our earliest projects. There are many reasons to practice the demo but one of the biggest benefit is catching missed requirements. I couldn't list the number of times one team member would demonstrate a feature only to realize they had left something out or made a simple mistake, usually before the person they were demonstrating to could even catch it. The simple act of walking through the feature &lt;em&gt;slowly&lt;/em&gt; proved immensely beneficial.&lt;/p&gt;

&lt;h2&gt;We Take Responsibility&lt;/h2&gt;

&lt;p&gt;Have you ever been in a meeting, or meetings, trying to determine the root cause of an expensive defect?  Development, QA and management are all represented, and then somebody will ask QA:&lt;/p&gt;

&lt;p&gt;&quot;So how come YOU didn't catch this bug?&quot;&lt;/p&gt;

&lt;p&gt;It's said with an accusatory emphasis on YOU because after all it's QA's job to catch bugs and if they aren't catching them, then what are they doing? The poor QA lead fumbles around, mentions they'll add a test for it so it won't happen next time, and development goes on doing the same thing they've always done.&lt;/p&gt;

&lt;p&gt;Of course the question should be:&lt;/p&gt;

&lt;p&gt;&quot;Why did you write that bug?&quot; or better &quot;Why did you ALL miss this bug?&quot;&lt;/p&gt;

&lt;p&gt;And the one and only correct answer to this question is really quite simple:&lt;/p&gt;

&lt;p&gt;&quot;We screwed up.&quot;&lt;/p&gt;

&lt;p&gt;You may substitute your own colorful phrasing if you like, but the point is the same. If a mistake was made it's because development made it, not because QA didn't see it, and ideally it's more than one person on the development team. 8th Light backs this up this responsibility in the most clear way possible, financially. On the craftsmanship page of 8th Light's website you'll find the following quote:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;If we make a mistake, we'll fix it quickly—for free. It's that simple.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I've told other people we do this. I've told them stories about us working for free for days or weeks at a time while we correct large errors. Usually they claim we're idiots, that we'll lose all our money fixing defects for free, because they accept the premise that large amounts of defects are inevitable.&lt;/p&gt;

&lt;p&gt;As evidence to the contrary I point out that we've had this policy for as long as I've been an employee, and in that time we've grown from 5 employees to 40. It seems to be working out just fine.&lt;/p&gt;

&lt;h2&gt;We Do Not Tolerate Preventable Defects&lt;/h2&gt;

&lt;p&gt;Why do we &quot;manage&quot; defects? The Wikipedia entry comparing &lt;a href=&quot;http://en.wikipedia.org/wiki/Comparison_of_issue-tracking_systems&quot;&gt;issue-tracking systems&lt;/a&gt; has 53 entries in it. Do you know what the best issue tracking system is? A piece of paper, with the defects written on it. Cross them off as you fix them. The end. Why the complications of classification, prioritization, assignment and all that?&lt;/p&gt;

&lt;p&gt;It only makes sense if we aren't going to fix them all. We're going to to tolerate many of them, but not &quot;too&quot; many, so we have complicated systems to track them all. I find the notion offensive both to my client and to the end user.&lt;/p&gt;

&lt;p&gt;A simple todo list works just fine if you actually fix the defects. On my projects I've found the best system is to stick defects in the next iteration.  Sometimes you can fix them all in a given week, sometimes you can't, and if you find you don't regularly get to 0 known defects then you've got to bite the bullet and and do nothing but fix defects for a while. Yes this will delay features shipping occasionally, but working in nearly bug-free code will mean you work faster the overwhelming majority of the time.&lt;/p&gt;

&lt;p&gt;I've worked on products with hundreds, even thousands of open defects. These defects were usually tolerated because individually they were small, but collectively they made for extremely slow development and angry customers. When you've reached that number of defects you really only have one option, a complete rewrite. I assure that's far more expensive than occasionally moving out a ship date by a week or two to fix bugs.&lt;/p&gt;

&lt;h2&gt;Real World&lt;/h2&gt;

&lt;p&gt;Whenever I get on my soapbox about defects people start giving me reasons why it can't work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&quot;You'd never ship!&quot;&lt;/li&gt;
&lt;li&gt;&quot;What about performance?&quot; or &quot;memory?&quot; or &quot;a feature they didn't like but works?&quot;&lt;/li&gt;
&lt;li&gt;&quot;Your customers will take advantage of you!&quot;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The truth is most of the time these problems take care of themselves. We can argue all day on the edges of it, in particular what it means for something to be a bug, and someday I may write about it, but my answer now is to say I work for a company that fixes every defect.  And it works.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Stable Sorting in Ruby</title>
   <category term="will-warner" />
   <link href="http://8thlight.github.com/will-warner/2013/03/26/stable-sorting-in-ruby.html"/>
   <author>
     <name>will warner</name>
   </author>
   <updated>26 Mar 2013</updated>
   <id>http://8thlight.github.com/will-warner/2013/03/26/stable-sorting-in-ruby</id>
   <content type="html">&lt;p&gt;The process of learning Ruby has brought several surprises. Some of which are more pleasant than others. The latest I have encountered is that &lt;code&gt;Array#sort&lt;/code&gt; is not stable. For those readers who may not understand what I mean by a this, this post starts with a description of what stable sorting is. From there it continues to show why sorting in Ruby is not stable. Lastly I will show a solution to this problem and explain why it works.&lt;/p&gt;

&lt;h2&gt;What is a stable sort?&lt;/h2&gt;

&lt;p&gt;Given a list of compound elements, viz. elements which contain elements, we often need different sorts on different elements. We may even need to sort a list without knowing specifically what other sorts were done.  A stable sort preserves the order of a previous sort.&lt;/p&gt;

&lt;p&gt;Let’s consider an example. Say we have a list of compound elements, each of which contains the elements “First Name” and “Last Name”:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;First Name&amp;nbsp&amp;nbsp&amp;nbsp&lt;/th&gt;&lt;th&gt;Last Name&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pat&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Shawn&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Dana&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Terry&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;&lt;/br&gt;
Suppose we wanted to sort this list using the last name as a primary key and the first name as a secondary key. In other words, all the entries with one last name would come before those with another last name, and for those last names which are the same, the entries would be sorted by first name.&lt;/p&gt;

&lt;p&gt;To accomplish this, we would first sort by the secondary key:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;First Name&amp;nbsp&amp;nbsp&amp;nbsp&lt;/th&gt;&lt;th&gt;Last Name&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Dana&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pat&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Shawn&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Terry&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;&lt;/br&gt;
Then we would sort by the primary key. If the second sort is stable, we get the following result:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;First Name&amp;nbsp&amp;nbsp&amp;nbsp&lt;/th&gt;&lt;th&gt;Last Name&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Shawn&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Terry&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Dana&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pat&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;&lt;/br&gt;
However, if the second sort were not stable, the order of the first names would be disturbed, and we might get a result more like this:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;First Name&amp;nbsp&amp;nbsp&lt;/th&gt;&lt;th&gt;Last Name&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Shawn&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Terry&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Jones&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Alex&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Dana&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pat&lt;/td&gt;&lt;td&gt;Smith&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;&lt;/br&gt;&lt;/p&gt;

&lt;h2&gt;Sorting in Ruby&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Array#sort&lt;/code&gt; method in Ruby uses the venerable &lt;em&gt;Quicksort&lt;/em&gt; algorithm. In its best case, Quicksort has time complexity O(n &lt;em&gt;log&lt;/em&gt; n), but in cases where the data to be sorted is already ordered, the complexity can grow to O(n&lt;sup&gt;2&lt;/sup&gt;). What we gain in efficiency by using Quicksort, we lose in stability. To make matters worse, some implementations randomize the data before beginning the sort to avoid this worst case for time complexity.&lt;/p&gt;

&lt;p&gt;Other sorting algorithms, such as merge sort and bubble sort, are stable. However they are not as efficient. Wikipedia has a good &lt;a href=&quot;http://en.wikipedia.org/wiki/Stable_sorting_algorithm#Stability&quot;&gt;comparison&lt;/a&gt; of sorting algorithms.&lt;/p&gt;

&lt;p&gt;To see an example of how &lt;code&gt;Array#sort&lt;/code&gt; is unstable, let’s fire up the Ruby REPL and create some data:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shuffle&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We now have a randomized list of six items, each of which contains two elements. If we treat the second element as a secondary key and sort on it, we get:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;But then if we take the result of this sort, and sort again on the first element as a primary key, the results may not be what we want:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Stabilizing Ruby Sorts&lt;/h2&gt;

&lt;p&gt;After a few minutes of Google searching, I ran across a &lt;a href=&quot;http://moserei.de/2008/09/18/stable-array-sorting-in-ruby.html&quot;&gt;blog post&lt;/a&gt; with an extension to the Ruby &lt;code&gt;Array&lt;/code&gt; class:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Array&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;stable_sort&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sort_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If, like myself, you are not content with accepting this as more Ruby magic, allow me to explain my understanding of how it works. This code is based on the &lt;code&gt;Enumerable#sort_by&lt;/code&gt; method, and the default comparison operator for &lt;code&gt;Array&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Enumerable#sort_by&lt;/code&gt; starts by creating a new array. Each element of the array has two elements. The first is the key given to sort the elements of the original array. The second is the element from the original array corresponding to that key. Returning to our earlier array of data, if we wanted to sort by the second element, &lt;code&gt;sort_by&lt;/code&gt; starts by creating an intermediate array:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It then sorts this intermediate array on the first element of each of the new elements:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediate2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And extracts the second element of each of the new elements to get the result:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intermediate2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To see how the &lt;code&gt;stable_sort&lt;/code&gt; method works, let’s walk through how it would operate on our array. First, we sort on the second element:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;007&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Next we create a new array using the first element of the partially sorted array and our monotonically increasing variable:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; 
&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then we sort on the first element of our new array:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;010&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The reason this works is because the default comparison operator for &lt;code&gt;Array&lt;/code&gt; uses the first element of the array as the primary key, the second as the secondary key, and so on. When we created this sort, we built the sort key using a monotonically increasing variable, preserving the order of the elements in the original array whose first elements are equal. In other words, we made the sort stable.&lt;/p&gt;

&lt;p&gt;The final step is of course to extract the elements in which we are interested:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p385&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;011&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This leaves us with the order from the first sort preserved. By understanding that &lt;code&gt;Array#sort&lt;/code&gt; may not give the results we expect, how &lt;code&gt;Enumerable#sort_by&lt;/code&gt; works, and how the default &lt;code&gt;Array&lt;/code&gt; comparison operator works, we can get stable sorts out of Ruby.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We are Principled: 5th Edition</title>
   <category term="lihsuan-lung" />
   <link href="http://8thlight.github.com/lihsuan-lung/2013/03/25/we-are-principled-5.html"/>
   <author>
     <name>lihsuan lung</name>
   </author>
   <updated>25 Mar 2013</updated>
   <id>http://8thlight.github.com/lihsuan-lung/2013/03/25/we-are-principled-5</id>
   <content type="html">&lt;blockquote&gt;
  &lt;p&gt;
    We teach anyone with a willingness to learn.&lt;br /&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;We do not hoard our knowledge or practices.
    &lt;cite&gt;&amp;mdash;from the 8th Light Principles of A Community of Professionals&lt;/cite&gt;
  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  European scholars often cite the inventions of the &lt;abbr&gt;compass&lt;/abbr&gt;, &lt;abbr&gt;gunpowder&lt;/abbr&gt;, &lt;abbr&gt;papermaking&lt;/abbr&gt;, and &lt;abbr&gt;printing&lt;/abbr&gt; as the major scientific and technological achievements of Ancient China. Yet there was another invention that perhaps influenced the Chinese civilization in a more profound way.
&lt;/p&gt;

&lt;p&gt;
  In the 1st century BC, Ancient China completed its first unification. This transformed a land divided by vassal states into an empire under a single regime. However, even after conquering the feudal lords who relinquished their reigns over the vast territory of China, the imperial family struggled to find enough educated men to fill the government overseeing the enormous population of their empire.
&lt;/p&gt;

&lt;p&gt;
  The &lt;abbr&gt;Han&lt;/abbr&gt; dynasty (206 BC – 220 AD) first appointed administrative officials based on recommendations from aristocrats, who had every interest to keep the power within their own clans. A &lt;abbr&gt;Han&lt;/abbr&gt; emperor eventually devised an imperial examination, based on Confucian classics, to evaluate the quality of his candidates. The emperor later offered the examination to the general public, and granted any aspiring individual a chance to climb the social ladder and join the ranks of the bureaucracy.
&lt;/p&gt;

&lt;p&gt;
  The imperial examination system was the first set of standardized tests based on merit in human history. This, subsequently gave birth to the &lt;i&gt;Chinese Dream&lt;/i&gt;, the story of an underprivileged boy spending his entire youth studying Confucius' writings and other Chinese classics, supported by his family and spouse, in the hope of one day becoming a well-respected elite civil servant appointed by the emperor.
&lt;/p&gt;

&lt;p&gt;
  By giving its people the motivation to send their children to schools and acquaint themselves with Confucianism, a preferable set of ideals for sustaining a society in harmony, the imperial family of China was able to scale up their administration for one of the largest populations on earth.
&lt;/p&gt;

&lt;p&gt;
  As a consultancy, we are fortunate to be living in a time when our service is in great demand. However, we are also faced with the same predicament as those Ancient Chinese rulers. We need more people to expand our business, and we only want to work with the best software professionals to uphold the standard of quality we demand for our service.
&lt;/p&gt;

&lt;p&gt;
  For our recruiting, we draw talent from as wide a pool as we can find. We accept applications regardless of their academic background or experience level. We have had apprentices with a Master's degree in Computer Science, a Bachelor's degree in Fine Arts, dropped out of college, or never went to college. We have had apprentices with more than twenty years of industry experience, and with no experience. We host &lt;a href=&quot;http://university.8thlight.com/&quot; target=&quot;_blank&quot;&gt;8th Light University&lt;/a&gt; every Friday at noon in our offices and welcome all to come for lunch and lessons, completely free of charge. We also organize the annual &lt;a href=&quot;http://scna.softwarecraftsmanship.org/&quot; target=&quot;_blank&quot;&gt;Software Craftsmanship North America&lt;/a&gt; conference to spark the interest of the public in what we do. We pride ourselves in our dedication to teach anyone with a willingness to learn. We work to enable the dreams of those who want to be software professionals despite their natural advantages or disadvantages. We reward those who stick to their dreams and never give up in the face of all the challenges along their journey.
&lt;/p&gt;

&lt;p&gt;
  Just as the Ancient Chinese rulers who had to look past the privileged class to find the right people to run their government, the 8th Light apprenticeship program is a crack in the ivory tower of software engineering that will shatter boundaries and invite a new generation of programmers to build a more diverse future.
&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>The Repository Pattern</title>
   <category term="mike-ebert" />
   <link href="http://8thlight.github.com/mike-ebert/2013/03/23/the-repository-pattern.html"/>
   <author>
     <name>mike ebert</name>
   </author>
   <updated>23 Mar 2013</updated>
   <id>http://8thlight.github.com/mike-ebert/2013/03/23/the-repository-pattern</id>
   <content type="html">&lt;p&gt;A recurring theme on the 8th Light blog is decoupling your applications from the implementation of details. A detail can be anything from UI elements to a database or even a framework. Decoupling your application from the details provides you with the ability to defer implementation decisions until you have a clearer picture of what you really need. In addition, decoupling has the added benefit of making it easier to test your system. In this post I want to take a closer look the Repository Pattern as a way that we can decouple ourselves from one of those details: the database.&lt;/p&gt;

&lt;p&gt;Right now you might be having the same reaction I did when I first heard the database referred to as a detail. It was something along the lines of, &quot;WHAT? but every application &lt;em&gt;needs&lt;/em&gt; a database! That's where all the stuff goes- how can it be a detail?!?&quot;&lt;/p&gt;

&lt;p&gt;Of course we do need data, but the decision on &lt;em&gt;how&lt;/em&gt; we're going to store that data is most certainly a detail, i.e. do we use MySQL, PosgreSQL, Redis, flat-files- or even a combination of different options.  And there is a huge benefit in delaying that decision as long as possible because it allows us more opportunities to learn &lt;em&gt;how&lt;/em&gt; we're going to use the data and the ways that we'll need to access it. Then we can make informed decisions about the details of our database(s).&lt;/p&gt;

&lt;h3&gt;It's Just An Interface&lt;/h3&gt;

&lt;p&gt;At its core the Repository pattern is a simple interface. It exists as a layer between your application and your data source so that your application doesn't need to be concerned with the implementation of data storage. As illustrated in the diagram below, instead of talking directly to the database your application uses the interface created by the Repository. It doesn't matter whether I'm using PostgreSQL or a simple in-memory data structure.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://lh3.googleusercontent.com/-qi-NuuwrYLk/UQky4BhNfmI/AAAAAAAACGY/1jravXBHLzg/s566/RepositoryDiagram.png&quot; alt=&quot;Repository Diagram&quot; /&gt;&lt;/p&gt;

&lt;h3&gt;Repository, meet Sinatra&lt;/h3&gt;

&lt;p&gt;Following is a scaled-down example of how I implemented a Repository in a &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; application. It was a small internal application and we implemented the Repository pattern for a couple of reasons: 1) I wasn't sure what database I was going to use, and 2) I wanted to keep my tests fast by using in-memory objects.&lt;/p&gt;

&lt;p&gt;The first thing I needed for my app was a User, so I created a &lt;code&gt;MemoryRepository::UserRepository&lt;/code&gt; that allowed me to store and retrieve a new user for the purposes of testing and development. (Since I'm storing everything in memory all of the &lt;code&gt;records&lt;/code&gt; are just User objects in a hash with unique ids.)&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MemoryRepository&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserRepository&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt;       &lt;span class=&quot;vi&quot;&gt;@records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt;       &lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;model_class&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;       &lt;span class=&quot;ss&quot;&gt;MemoryRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:User&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;model_class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt;       &lt;span class=&quot;vi&quot;&gt;@records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19&lt;/span&gt;       &lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20&lt;/span&gt;       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;22&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;23&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_by_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;24&lt;/span&gt;       &lt;span class=&quot;vi&quot;&gt;@records&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;25&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;26&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;27&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;There shouldn't be any surprises in these methods, except for maybe &lt;code&gt;model_class&lt;/code&gt;, which is used again in the &lt;code&gt;new&lt;/code&gt; method. My repository's primary responsibility is the storage of objects so it shouldn't really need to be concerned with the creation of those objects. However, there was a good chance we were going to end up using DataMapper or ActiveRecord so it wouldn't be unreasonable for someone to ask the datastore class to create a new object of the type it holds. So in order to make sure that there weren't any surprises for future devs the &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;model_class&lt;/code&gt; methods delegate that request to the User object.&lt;/p&gt;

&lt;p&gt;Next up is the &lt;code&gt;Repository&lt;/code&gt; class. This is the class that my app talks to and allows it to be blissfully unaware of any datastore on the other side. Again, I'm just using a simple hash table and I can &lt;code&gt;register&lt;/code&gt; any repository I want to use and then call it with &lt;code&gt;for&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Repository&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;repositories&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;repositories&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;     &lt;span class=&quot;vi&quot;&gt;@repositories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;      
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;repositories&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In order to tie the Repository and my UserRepository together I add the following lines to an &lt;code&gt;environment.rb&lt;/code&gt; file (or a config file or just at the top of the main Sinatra app):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:development&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt;   &lt;span class=&quot;no&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;MemoryRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:UserRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now to put it to use. Let's say I have a Sinatra action that takes a user to their home page and I want to grab that user from the database so I can say &lt;code&gt;&quot;Hello, #{@user.name}&quot;&lt;/code&gt;. Here's how I use my Repository to do that:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/user/:id&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt;   &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_by_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;3&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;erb&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/users/show&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_sum&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Exciting, right? Well, not really, at least not yet. The real action happens as soon as we decide to implement another data store for Users.&lt;/p&gt;

&lt;p&gt;For production I ended up using DataMapper wired to PostgreSQL.  Using RSpec's 'shared_examples' (which I've written about &lt;a href=&quot;http://mikeebert.tumblr.com/post/28600693251/repository-pattern-day-2&quot;&gt;here&lt;/a&gt;) I was able to quickly build out my new &lt;code&gt;DatamapperRepository::UserRepository&lt;/code&gt; based on the methods I'd already implemented in my in-memory &lt;code&gt;UserRepository&lt;/code&gt; class.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;lineno&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;DataMapperRepository&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserRepository&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;model_class&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4&lt;/span&gt;       &lt;span class=&quot;ss&quot;&gt;DataMapperRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:User&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt; 7&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;model_class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13&lt;/span&gt;       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15&lt;/span&gt;  
&lt;span class=&quot;lineno&quot;&gt;16&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_by_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;model_class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you can see, a few of the methods changed, but my main application code knows nothing about the data stores and won't change at all. It will continue to just call &lt;code&gt;Repository.for(:user)&lt;/code&gt;, and as long as every new UserRepository class implements the same methods then it doesn't matter what's on the other side.&lt;/p&gt;

&lt;p&gt;The only lines of code I need to add to start using the new UserRepository in are back in my environment file:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;lineno&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:production&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;2&lt;/span&gt;   &lt;span class=&quot;no&quot;&gt;Repository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;DatamapperRepository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:UserRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Mix &amp;amp; Match Databases&lt;/h3&gt;

&lt;p&gt;I won't go into detail, but the other real advantage of the Repository pattern is if I decided to use different types of databases. For example, consider if I used PostgreSQL for Users and then Redis for &quot;Tasks&quot;. Would my main application care? Nope, it would just continue to call &lt;code&gt;Repository.for(:user)&lt;/code&gt; and &lt;code&gt;Repository.for(:task)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Conclusion and Other Resources&lt;/h3&gt;

&lt;p&gt;This was of course a very simple example of the pattern, but you can see more complex (and real-world) samples in a pair of 8th Light's open source projects. The repositories that we use for an internal project were split out into gems and you can look through those on Gihtub at the &lt;a href=&quot;https://github.com/8thlight/artisan-repository&quot;&gt;artisan-repository&lt;/a&gt;, &lt;a href=&quot;https://github.com/8thlight/artisan-memory-repository&quot;&gt;artisan-memory-repository&lt;/a&gt; and &lt;a href=&quot;https://github.com/8thlight/artisan-ar-repository&quot;&gt;artisan-ar-repository&lt;/a&gt; (for ActiveRecord). Several 8th Light craftsmen have also contributed to &lt;a href=&quot;https://github.com/8thlight/hyperion&quot;&gt;Hyperion&lt;/a&gt;, which provides a uniform API to multiple datasources using simple key-value pairs. (Hyperion even takes most of the leg work out of setting up the Repository pattern.)&lt;/p&gt;

&lt;p&gt;Like any design pattern, it really only makes sense to implement the Repository pattern if you have a good reason. However, the ability to defer your database decision until you've had the chance to learn &lt;em&gt;how&lt;/em&gt; you're going to use your data, and then painlessly change it if your needs change, seems like a very good reason.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>There are Ladies Present</title>
   <category term="uncle-bob" />
   <link href="http://8thlight.github.com/uncle-bob/2013/03/22/There-are-ladies-present.html"/>
   <author>
     <name>uncle bob</name>
   </author>
   <updated>22 Mar 2013</updated>
   <id>http://8thlight.github.com/uncle-bob/2013/03/22/There-are-ladies-present</id>
   <content type="html">&lt;p&gt;Oh!  I think I &lt;em&gt;get it&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;I'm likely to take some heat for what I'm about to write; but I think I finally understand something that's been bothering me.&lt;/p&gt;

&lt;p&gt;Three times in the last few years I've been confronted by women who have been offended by something I said during a talk, or a class.&lt;/p&gt;

&lt;p&gt;The first time was at a Rails conference a few years ago.  I gave a talk entitled &quot;What killed Smalltalk could kill Ruby&quot;.  In that talk I referred to C++ as a &lt;em&gt;testosterone&lt;/em&gt; language, and I referred to Java as an &lt;em&gt;estrogen&lt;/em&gt; language.  I meant no harm by this statement.  I was mostly making a joke about how seriously C++ programmers take themselves compared to Java programmers.  However, some of the women in the audience were offended, and tweeted about their concern.  I quickly, and publicly, apologized because I &lt;em&gt;do not&lt;/em&gt; want the women in our industry to feel unwelcome.  I think we need them.  More on that later...&lt;/p&gt;

&lt;p&gt;Though I apologized, I was also rather puzzled.  My &quot;Tim - the tool man - Taylor&quot; persona was going: &quot;eeeuuuhhh?&quot;  What was it that was offensive about that testosterone/estrogen remark?  I didn't know it, but I had already answered that question.  I just didn't get it -- then.&lt;/p&gt;

&lt;p&gt;A few years later I was giving a talk at ACCU in Oxford.  The name of the talk was &quot;Requiem for C&quot;.  I started talking about COBOL.  And that caused the standard photo image of Admiral Grace Hopper, the inventor of COBOL, to pop into my mind.  In the photo she's standing tall in full dress uniform; including a navy cap.  Out of my mouth came the words &quot;in that cute little cap&quot;.  I wanted to grab the words back, but...&lt;/p&gt;

&lt;p&gt;After the talk Emily Bache came up to me and gently suggested that the statement was not helpful to all the women in the audience.  She reminded me that it's difficult enough to be a woman programmer without someone like me making jokes about other women.  I completely agreed, and published yet another apology.  I published it because &lt;em&gt;I want&lt;/em&gt; women in this industry.  I think we &lt;em&gt;need&lt;/em&gt; them.  More on this later...&lt;/p&gt;

&lt;p&gt;Just this week I was teaching a Clean Code course in London.  I had 30 students, four of whom were women.  I was very pleased with that because it's among the highest ratio of women I've had in class. The class went very well, and after three days I dismissed them all and read their evaluations.&lt;/p&gt;

&lt;p&gt;One of the women said she really enjoyed the class and learned a lot, but then wrote &quot;somewhat male oriented, which made me feel out of place&quot;.  Tim Taylor: &quot;Eeeuuuhhh?&quot;  What had I done? Even now I don't know what remark I may have made, or what off hand statement might have slipped.  I don't know what her trigger was.  But, because of something else that happened this week, I think I get it.&lt;/p&gt;

&lt;p&gt;The event I'm referring to is the fiasco at PyCon involving Adria Richards, Alex Reid, Playhaven, and SendGrid.  If you aren't aware of it, there's a reasonable history in &lt;a href=&quot;http://amandablumwords.wordpress.com/2013/03/21/3/&quot;&gt;Amanda Blum's blog&lt;/a&gt;.  I'm not going to weigh in on the debate about who was right or wrong in this mess, other than to say that the real villains are those jerkwads on the net executing DDOS attacks, and tweeting all manner of obscenities, personal attacks, and illegal threats.&lt;/p&gt;

&lt;p&gt;So what is it that I get now?  Well, let me tell you a few stories:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;When I was in 8th grade, a new kid came to our school.  He had been kicked out of his last school in a &quot;tougher&quot; part of the district, so he was an instant hero.  A real &quot;hood&quot;.  He would greet you for the first time by giving you a big smile with sparkling eyes while enticingly pointing his thumbs to his crotch.  As you inevitably looked down to follow his pointing gesture he’s say: &quot;Bagged ya!&quot;.  Ha, ha, ha.  Hee, hee, hee. Ho, ho, ho.  Nothing brightens the day like teenage boy humor.&lt;/p&gt;&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;blockquote&gt;&lt;p&gt;Twenty years ago I worked as a chief software architect at a network management startup.  I was in the mens room one day and two guys in business suits sidled up to the urinals.  As they finished one said to the other: &quot;Careful how you shake the dew off that lilly!&quot;  As if on cue the other responded: &quot;Any more that two shakes and you’re playing with it!&quot;.  Ha, ha, ha.  Hee, hee, hee.  Ho, ho, ho.  Nothing brightens the day like thirty-something urinal humor.&lt;/p&gt;&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;blockquote&gt;&lt;p&gt;I was watching ER one night a few years back and saw one beautiful young female doctor tell another beautiful young female doctor that she had dropped her recent boyfriend because he was &quot;orally challenged — he wouldn’t go south of the 32nd parallel — he didn’t want to eat at the Y.&quot;  Ha, ha, ha. Hee, hee, hee.  Ho, ho, ho.  Nothing brightens the day like the writers of a once great show succumbing to the temptation to use teenage urinal humor.&lt;/p&gt;&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;blockquote&gt;&lt;p&gt;My wife has become a fan of Grey’s Anatomy. I’ve watched a few of the shows and don't get the attraction. It's all about farting, cheating, screwing on every accommodating surface, pinning panties to bulletin boards, and having your ex-boyfriend teach you how to please your new lesbian lover. Ha, ha, ha. Hee, hee, hee.  Ho, ho, ho. Nothing brightens the day like 8 year-old potty humor.&lt;/p&gt;&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;All these things have one thing in common.  Locker room humor. I found it all offensive to my intelligence, offensive to my values, and just plain offensive -- &lt;em&gt;because of the context&lt;/em&gt;.  These things did not belong in the school, in the public men's room, or on TV in &lt;em&gt;my&lt;/em&gt; living room.  If they belonged anywhere, they belonged in a locker room.&lt;/p&gt;

&lt;p&gt;Have we created a locker room environment in the software industry?  Has it been male dominated for so long that we've turned it into a place where men relax and tell fart and dick jokes amongst themselves to tickle their pre-pubescent personas?  When we male programmers are together, do we feel like we're in a private place where we can drop the rules, pretenses, and manners?&lt;/p&gt;

&lt;p&gt;What if the roles were reversed?  What if women had dominated the software industry for years, and we men were the ones who were struggling to break into the industry?  Men, can you imagine how hard it would be if all the women were constantly, and openly, talking about tampons, cramps, yeast infections, cheating, being cheated on, Trichomoniasis, faking-it, etc?  I don't know about you, but It would make me feel out of place.  And there'd be no place to escape it, because the women would be &lt;em&gt;everywhere&lt;/em&gt;.  I'd want them to save that kind of talk for the ladies room.  I'd want them to remember that &lt;em&gt;men were present.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Men, perhaps we need to remember that &lt;em&gt;there are ladies present&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now that last phrase is going to get me into trouble because too many injustices have been enacted under the excuse that &lt;em&gt;ladies were present&lt;/em&gt;. I learned about this from a movie called &lt;em&gt;Dangerous Beauty&lt;/em&gt;.  The story was of a beautiful young &lt;em&gt;courtesan&lt;/em&gt; in 16th century Venice.  The important men of the city would take her on all their political and business outings, allowing her to know what their ventures were all about.  These men kept their wives entirely in the dark, because you didn't discuss things like that &lt;em&gt;when ladies were present&lt;/em&gt;.  So, to find out what was really happening to their husbands and their city, they consulted the &lt;em&gt;courtesan&lt;/em&gt;; and they used a phrase that has stuck with me ever since.  They said they were consigned to a life of &lt;em&gt;perpetual inconsequence&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Perpetual Inconsequence&lt;/em&gt;.  That sounds like prison to me.  That sounds like &lt;em&gt;hell&lt;/em&gt; to me.  If that's what being treated like a lady means, then to hell with that.&lt;/p&gt;

&lt;p&gt;I think this explains another experience I had at the Midwestern Ruby conference a couple of years ago.  Before I gave the closing keynote on Architecture, I was in the audience listening to a young man give a very interesting talk about GitHub.  But the talk bothered me greatly because there was an f-bomb in every sentence.  It was effing this and effing that and what the ef here and there and everywhere.  Then to top it off he talked about all the code he had written while being &quot;hammered&quot;.&lt;/p&gt;

&lt;p&gt;Then someone got up and gave another talk and mentioned that he was happy there were so many women in the audience, and that we needed more.  (He's right...  More on that later...).&lt;/p&gt;

&lt;p&gt;Then it was my turn.  I started the talk by saying: &quot;If we want more women to come to these events, and to participate in our industry, we need to be careful about dropping F-bombs.&quot;  Several of the men in the room applauded.  But one young woman stood up and yelled: &quot;&lt;em&gt;I totally fucking disagree!&lt;/em&gt;&quot;&lt;/p&gt;

&lt;p&gt;Tim Taylor: &quot;Eeeuuuhh?&quot;&lt;/p&gt;

&lt;p&gt;But now I think I get it.  If I may paraphrase:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&quot;Don't treat me differently in a professional context, because I won't stand for perpetual inconsequence.  If you drop f bombs in a professional context, don't stop just because I'm a girl.  But do remember that I'm a woman and there are things that men do that I don't care to listen to or be part of.  Save that for the men's room because it's making me crazy when you do it in front of me.&quot;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, to pay off my debt.&lt;/p&gt;

&lt;p&gt;I had the pleasure of spending an hour with Desi McAdam (@desi) one evening at a function where she happened to be working.  I told her that, to me, getting a program to work was like a conquest -- killing the beast and brining home the meat.  She replied that to her, it was more of a growing, nurturing experience of helping the software come into the world.  Tim Taylor: &quot;Eeeuuuhhh?&quot;&lt;/p&gt;

&lt;p&gt;But I've had some time to think about this.  When I think about code, I think about it in terms of killing bugs, molding the code into the shape I demand.  I am the master.  It is my slave.  I have the power to make it be what I want.  It's a huge power trip for me.&lt;/p&gt;

&lt;p&gt;I wonder if that isn't part of the craftsmanship and professionalism problem that our industry has.  Why is so much of our software so bad?  Is it because we treat it as a conquest?  Is it because we don't nurture it enough?&lt;/p&gt;

&lt;p&gt;Don't get me wrong, for me it'll always be a conquest.  I'll always be the master of my code.  That's who I am.  But perhaps that's not enough to turn our programming industry into a real profession.  Maybe we need the other attitude as well.&lt;/p&gt;

&lt;p&gt;After all, in my personal life, I know that the worst decisions I have ever made are the ones I've made without my wife. My wife completes me in many different ways.  Without her I'd be a shadow of a man walking through a two-dimensional space.  My relationship with her transformed me from a boy into a man.  She helped me to grow up.&lt;/p&gt;

&lt;p&gt;Our industry needs to grow up too.  Perhaps the reason we've had so much trouble maturing is that we don't have enough women to help us make the transition from child to adult.  Their attitudes, their ways of thinking, different from ours, may be the very things we need to complete us as an industry, and a &lt;em&gt;profession&lt;/em&gt;, &lt;em&gt;and as a craft&lt;/em&gt;.  It seems to me we need these women in order for our profession to &lt;em&gt;become&lt;/em&gt; a profession.&lt;/p&gt;

&lt;p&gt;So we need to make the women feel welcome.  We need to stop considering that the whole of the software industry is our private locker room.  We need to treat women programmers as professional peers, while also remembering that there are ladies present.&lt;/p&gt;

&lt;p&gt;Of course I'm just Tim Taylor, talking over the fence to Wilson.  Women, do I have this right?&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>We are Principled: 4th Edition</title>
   <category term="myles-megyesi" />
   <link href="http://8thlight.github.com/myles-megyesi/2013/03/18/we-are-principled-4.html"/>
   <author>
     <name>myles megyesi</name>
   </author>
   <updated>18 Mar 2013</updated>
   <id>http://8thlight.github.com/myles-megyesi/2013/03/18/we-are-principled-4</id>
   <content type="html">&lt;blockquote&gt;
  &lt;p&gt;
    We embrace differences of opinion and personality.&lt;br/&gt;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;We do not allow our current practice to impede improvement.
    &lt;cite&gt;&amp;mdash;from the 8th Light Principles of A Community of Professionals&lt;/cite&gt;
  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Strong opinions&lt;/h3&gt;

&lt;p&gt;Having a strong opinion about something means that you believe very deeply that you are right. For instance, I have a &lt;i&gt;strong&lt;/i&gt; opinion that black coffee is better than coffee with cream and sugar. After many years of experimenting, the idea has become solidified in my mind. When pressed to defend myself, I will argue passionately in favor of black coffee. Does this mean that I am right? Of course not. It simply means that through my experience, I have learned that I like black coffee best. On the other hand, &lt;i&gt;weak&lt;/i&gt; opinions are just the opposite. Our experiences have not led us empirically down the same path.&lt;/p&gt;

&lt;p&gt;At 8th Light, we often describe ourselves as &quot;opinionated&quot;. This means that we have &lt;i&gt;strong&lt;/i&gt; opinions about how software should be developed. We work in short iterations because it enables more feedback and transparency. We work in pairs because it provides more accountability. We test drive our code because it gives us more confidence in our solutions. We know and practice SOLID design principles because they lead to cleaner, more maintainable code.&lt;/p&gt;

&lt;p&gt;When I say that we feel strongly about these practices, it does not mean that we think all competing practices are &lt;i&gt;wrong&lt;/i&gt;. It simply means that our current practices are the most &lt;i&gt;useful&lt;/i&gt; we have found so far. Yet we also believe they can be improved.&lt;/p&gt;

&lt;h3&gt;Weakly held&lt;/h3&gt;

&lt;p&gt;Strong opinions are often held with a firm grip. This is natural. When we believe strongly that something is right, it can be hard to change our minds. With enough time however, this firm grip can turn into stubbornness. No amount of reasoning will change our minds. We no longer learn and grow from the experiences of others.&lt;/p&gt;

&lt;p&gt;At 8th Light, we hold our strong opinions weakly. They are strong opinions because they are strengthened by experience. They are weakly held because we continue to expiriment with an open mind. We do not maintain our current practices for the sake of our pride. Above all, we are most interested in what practices produce the best results for our clients. Sometimes this can be hard. It takes humility to admit that our practices could be wrong or lacking in places. But opening up to others and embracing their feedback is crucial to improvement.&lt;/p&gt;

&lt;p&gt;We hold our strong opinions weakly. We embrace diversity because it allows us to learn and grow from the experience of others and not just our own. Otto von Bismarck once said, &quot;A fool learns only from his own mistakes. The wise man learns from the mistakes of others.&quot;&lt;/p&gt;
</content>
 </entry>
 

</feed>
