Blog - Sandi Metzhttps://www.sandimetz.com/blog/Tue, 20 Feb 2018 17:48:06 +0000en-USSite-Server v6.0.0-15962-15962 (http://www.squarespace.com)What Does OO Afford?Sandi MetzWed, 21 Feb 2018 06:00:00 +0000https://www.sandimetz.com/blog/2018/21/what-does-oo-afford537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:5a80b06a8165f5c700a9796aGood Morning,
I've been thinking about the affordances of programming languages.
A Little Background
In my previous post, Breakin Up the Behemoth, I posited an explanation for
why OO apps so often evolve a few, disproportionally large, unmaintainable,
condition-filled classes. Unfortunately, that post didn't offer a cure for
this problem; it just gave the less-than-helpful advice that you avoid that
situation.
This post continues to explore the problem of classes that get too large.
My hope is by that learning to recognize the imminent appearance of the
big-class-slippery-slope you can avoid accidentally sliding down it.I've been thinking about the affordances of programming languages.

A Little Background

In my previous post,
Breaking Up the Behemoth, I posited an explanation for why OO apps so often evolve a few, disproportionally large, unmaintainable, condition-filled classes. Unfortunately, that post didn't offer a cure for this problem; it just gave the less-than-helpful advice that you avoid that situation.

This post continues to explore the problem of classes that get too large. My hope is by that learning to recognize the imminent appearance of the big-class-slippery-slope, you can avoid accidentally sliding down it.

Most of the ideas here are my opinion. Although this post starts out by examining a broad, general idea, I promise that it does, eventually, directly address object-oriented programming. Read on for the general introduction, or skip forward to the OO specifics, as your inclinations dictate.

Consider the Doorknob

So, what are affordances? Merriam-Webster defines
affordance
as "the qualities or properties of an object that define its possible uses or make clear how it can or should be used." Wikipedia's
affordance disambiguation page
more concisely suggests "a potential action enabled by an object."

"The affordances of the environment are what it offers the animal, what it provides or furnishes, either for good or ill. The verb to afford is found in the dictionary, the noun affordance is not. I have made it up. I mean by it something that refers to both the environment and the animal in a way that no existing term does. It implies the complementarity of the animal and the environment."--Gibson (1979, p. 127)
7

(I find myself tickled by his blithe confession that he just plain "made it up". If not for the fact that it would take us on a terrible tangent, I'd pause here and make up a few words myself.)

Here are a few real-world examples to illustrate the meaning of affordance.

Figures 1, 2 and 3 show examples of hardware used to unlatch and open doors. The different styles suggest different usages.

The knob in Figure 1 affords grasping by hand, turning to unlatch, and pulling to open. While it clearly offers grasping, turning, and pulling, it supplies no information about whether pulling on the knob will actually open the door. Even though the knob clearly can be pulled, pulling on it might not give you the results you want.

The lever in Figure 2 differs from the knob in that it affords unlatching by pushing up or down by any means, so you can use it even if your hands are occupied or unavailable. I have levers in my house and can highly recommend them. They are way more convenient than round knobs.

Figure 3 is Figure 2 but with an added sign to solve the push-or-pull-to-open-the-door problem. The need for a sign suggests that the design of the hardware fails to offer a complete set of affordances. If the usage were clear from the design, there'd be no need for additional, written directions.

In contrast, here are three other styles of door hardware. The first thing to notice is that these options are just for opening the door, that is, they don't have any responsibility for unlatching.

The styles above convey all of the information you need to use them correctly. The ring in Figure 4 makes it obvious that the door swings towards you, i.e. that pulling opens the door.

The reason Figure 5 feels wrong is that it's over-specified. The handgrip suggests pulling so strongly that the sign feels a bit insulting.

The push plate in Figure 6 is what you'd expect to find on the opposite side of the doors shown in Figures 4 and 5. Plates very obviously afford pushing, and so pair naturally with things that want to be pulled.

Different Programming Languages Offer Different Affordances

Just like varying styles of doorknob, different programming languages offer their own unique affordances. Language designers have preconceived ideas about the best way to model reality, and their creations reflect these biases. Thus, programming languages are explicitly designed to "enable" certain kinds of thinking.

I'm talking about something that's deeper than syntax. Languages have points-of-view: they're designed to be used in certain stylized ways. The mere fact that code compiles doesn't mean it's arranged as the language designer intended.

While it's possible to warp most any programming language into use by an alternate way of thinking, working at cross-purposes from your language's intentions is not the most efficient way to write code. Don't roll this rock uphill. If you don't understand your language's affordances, learn them. If your coding inclinations conflict with the designer's biases, yield.

And that brings me to OO, and why big classes evolve.

The Affordances of OO

I believe that OO affords building applications of anthropomorphic, polymorphic, loosely-coupled, role-playing, factory-created objects that communicate by sending messages.

Let's break that down. :-)

Anthropomorphic
means "ascribing human characteristics to nonhuman things." This means that we think of our objects as having volition, desires, and agency just as if they were people. We don't write algorithms and arrange them in namespaced classes: instead we create new worlds where things, concepts, and even ideas are virtual beings with whom we can converse.

Polymorphic
means "having many forms." Imagine that you (I'm anthorpomorphising here, so by "you" I mean "the application" or "an object in the application") have different classes that provide their own unique responses ("forms") for a common set of messages. Instances of these classes conform to the same API and so play a common "role."

When collaborating with an object that plays a role, you clearly have to know what messages you can send, but you don't need to know anything about what happens "over there" where the message is received. You, as the message sender, know "what" you want, and the role-playing object with whom you are collaborating is responsible for supplying one specific "how".

So, messages provide a level of indirection between what you want to do and how it actually gets done, and polymorphism lets you define alternative ways of doing things.

The next idea to add to your OO mindset is loose-coupling.

An example of tight-coupling is when you know the name of a class with whom you intend to collaborate. This knowledge causes you to depend on that other class name; if it changes, you must change. Even worse, if a new class arises that can play the same role as your current collaborator (i.e. it polymorphically implements that same API), you can't talk to the new one because you're tightly coupled to the original.

The coupling problem is exacerbated when you need to collaborate with a specific player of a role rather than with an instance of a known class. Tight-coupling to a role not only forces you to know many different class names, it also requires that you know how to select the correct one.

Coupling can be loosened by separating the place where objects get created from the place where they get used. For example, instead of creating your own collaborators, someone else could create them and pass them to you. This is called "dependency injection."

Dependency injection is not scary. Think of it as a simple technique that adds a level of indirection so that formerly connected objects can vary independently.

Becoming comfortable with dependency injection requires that you let go of that unseemly desire to know exactly what it is that other objects do. OO asks you to blithely trust others to correctly do their bit. It wants you to strive for ignorance to protect yourself from the consequences of distant changes to other objects.

Of course, in order to have a dependency to inject, someone--somewhere--has to create the right object. If creating the right object requires a conditional, this should happen in a "factory."

Factories? Also not scary. A factory is merely an easily accessible method that knows everything necessary to create the right object for a given situation. Factories allow you to isolate conditionals that would otherwise be duplicated in many places.

Leveraging OO's Affordances to Avoid Big Classes

In my opinion, large, condition-laden classes reveal failures of the OO mindset. The conditionals in these large classes often switch on something that could be thought of as a type. Please note that I'm not asserting that these conditionals actually test against real class names--they often don't. Rather, I'm suggesting that the conditionals exist for a reason, and that many times the reason is a concept or idea that could have been modeled as a real thing within the virtual world of your app. The conditionals about which I'm concerned are those that suggest the existence of model-able abstractions, regardless of whether or not these abstractions have been officially codified into classes.

The OO mindset interprets the bodies of the branches of these "type"-switching conditionals as pleas for you to create objects that polymorphically play a common role.

The OO mindset understands the switching logic of these conditionals to be a petition for you to isolate object creation in a factory.

And the OO mindset regards the mere presence of a type-switching conditional as a heartfelt request that you replace the entire thing with a simple message sent to an injected, factory-created, role-playing object.

This is what OO affords. It wants you to replace your procedural monoliths with collections of small, independent, collaborative objects. The existence of a large, condition-laden class signals that the procedural code has failed you. When you see such an object, it's time to change mindsets.

Best,Sandi

News:

Public POOD course in May in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change your mindset about objects.

]]>Breaking Up the BehemothSandi MetzWed, 13 Sep 2017 11:00:00 +0000https://www.sandimetz.com/blog/2017/9/13/breaking-up-the-behemoth537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:59b6d763be42d622146143d3I've been thinking about how applications evolve, and what we might do if
we're unhappy with the results. Three apparently unrelated ideas have been
percolating in my head. In this newsletter I'll introduce each one and
connect them together, in hopes that understanding these connections will
help us understand our apps.Good Morning,

I've been thinking about how applications evolve, and what we might do if we're unhappy with the results. Three apparently unrelated ideas have been percolating in my head. In this newsletter I'll introduce each one and connect them together, in hopes that understanding these connections will help us understand our apps.

These thoughts are very definitely my opinion, justified only by past experience. YMMV, but it'll give you something to think about. :-) Expect lots of pictures. Imagine me waving my arms and drawing on the whiteboard.

#1: Design Stamina Hypothesis

The vertical axis above represents cumulative functionality. The higher the line, the more that got done. The horizontal axis represents time passing. The further to the right, the later in time.

Two different lines are plotted. The orange line illustrates how much functionality you will produce by any point in time by investing in design from day one. The blue line illustrates the outcome if you defer serious design. Notice that the blue line rises fastest early on, but that the orange line eventually overtakes it.

The Design Stamina Hypothesis suggests that early on in a project you'll get more done if you don't bother too much with design, but a point will come when you'll be better off if you invest some energy into it.

The next idea is about the difference between procedural and object-oriented code.

#2: Procedural vs Object-Oriented Code

The section compares procedural and OO code in terms of changeability and understandability. The following graph explores the trade-offs between the two.

Figure 2

Changeability is plotted on the vertical axis above. Code that is easier to change goes at the bottom. Code that is harder to change, at the top.

Understandability is represented by the horizontal axis. Easier to understand code goes to the left, harder to understand code, to the right.

A simple procedure is merely a list of steps. Simple procedures are easy to understand and easy to change, and so fall into the lower left part of the graph above. This is the most cost-efficient place to be.

For some problems, simple, non-conditional, non-duplicated procedural code is the best solution. What could be cheaper? Write the code and run.

Over time, however, the situation might change. A new feature request might force the addition of conditional logic, or the duplication of parts of the solution in other places, leading to Figure 3 below.

Figure 3

Above, the cost-effective procedure has morphed into a complicated, condition-laden, duplicative morass of code that's hard to understand or change.

Simple procedures are cheap. Complicated procedures are expensive. The only compliment you can pay a complicated procedure (and this is really scraping the bottom of the barrel) is to say that at least all of the #$%@! code is in one place. However, proximity alone is not enough to justify this complexity. There are more cost-effective ways to arrange code.

The next graph adds object-oriented code to the mix. Notice that the OO solution is a little more costly than a simple procedure but far less costly than a complex one.

Figure 4

In object-oriented solutions, small, interchangeable objects collaborate by sending messages. Messages afford seams which allow you to replace existing objects with new ones that play the same role. Message sending makes it easy to change behavior by swapping in new parts.

Another consequence of message sending is that it obscures the details of what happens as a result. From the senders' point of view, a message represents only an intention. The receiver of the message supplies the implementation, which is hidden from the sender. Messages bestow effortless local substitutability at the cost of ignorance of distant implementation.

Relative to complex procedures, OO is easier to understand and change. Relative to simple procedures, OO can be as easy to change, but might well be harder to understand as a whole.

So, OO isn't a slam-dunk, hands-down winner. It depends on the complexity of your problem and the longevity of your application.

#3: Churn and Complexity

Churn is interesting in isolation, but it's even more useful to consider churn alongside complexity. Feathers' article includes the following chart, to which I've added the curved green line.

Figure 6

Above, churn is on the horizontal axis. Code complexity is on the vertical.

Complicated code that rarely changes appears in the upper left quadrant of this graph. We abhor complication, but if the code never changes, it's not costing us money. Pretend the code is a cabinet overstuffed with teetering Tupperware: just quietly press the door closed and sneak away. Ignore code in this quadrant until it starts to churn.

Simple code that changes a lot falls into the lower right quadrant. If code is simple enough (think configuration file) it will be cheap to change. Change this code as often as necessary, as long as it remains simple.

The lower left quadrant contains things that aren't very complicated and that don't change much, so this code is already cost-effective and can also be ignored.

The green line curves from upper left, through lower left, and into lower right. This illustrates the curve around which we'd like the code in our apps to cluster. Notice that this line does not enter the top right quadrant.

The top right quadrant reflects complicated code that changes often. By definition, code like this will be hard to understand and difficult to change. We'd prefer this quadrant to be empty, so code that slithers into it should be refactored right back out of it.

Now that we have these ideas in common, I can lean on them to explain a way in which applications go wrong.

Code Evolves Towards a Predictable Kind Of Mess

There's a kind of code mess I see repeatedly, where'er I travel. Courtesy of Code Climate, here are a few graphs that expose its symptoms in several projects (as of Sept 7, 2017).

Figure 7

Figure 8

Figure 9

The Churn vs. quality charts above are Code Climate's variant of Michael Feathers' File Churn vs. Complexity idea. Notice that on each of these charts the points cluster around a curve similar to the green line in Figure 6. This is good. It is commendable that in these applications most of the complex code changes little, and most changes are to simple code.

However, each of these graphs also contains an unwanted outlier that resides in the top right quadrant. I'm not familiar with the source code for these apps, but sight unseen I feel confident making a few predictions about the outlying classes. I suspect that they:

are larger than most other classes,

are laden with conditionals, and

represent core concepts in the domain

You can verify this, if you care to, by clicking on each graph above to open the corresponding page in Code Climate. Once there, click on the dot of the outlier to link to the underlying code. As I've already confessed, I don't really know these apps so what do I know...but based on name, size, complexity and churn, I can't help but believe I'm correct. If I'm wrong, or if the code has changed by the time you look, just ignore the example and continue to trust the principle. :-)

Many applications express this pattern. Much of their code is fairly understandable and reasonably easy to change. However, they also contain one or two large, complex, and constantly churning classes that represent extremely important ideas in their domain.

Everyone hates working on these outlier classes. To touch them is to break them. The tests don't provide safety. And no attempted cure helps. Despite best efforts, these classes continue to grow in size and complexity. They're headed from bad to worse.

How does this happen?

We can use a combination of the first three ideas to explain the problem, and understanding the problem offers hope of preempting it.

I posit the following:

If you do design too early, you'll waste your efforts.
(Figure 1 - The orange line early in time)

If you never do design, your code will become a painful mess.
(Figure 1 - The blue line late in time)

A time will come when investing in design will save you money.
(Figure 1 - Where the lines cross)

This is how we end up with applications where many small, efficient classes coexist alongside one costly, massive, condition-filled behemoth. A series of small, innocent changes turned the application's most important code into a class so complex that no one could fix it. The problem becomes visible at item 15 above, but its roots lie in item 8, where tiny bits of complexity got added, repeatedly, until the logic passed the point of no return.

Sticking with procedures too long is just as bad as doing design too soon. If important classes in your domain change often, get bigger every time they change, and are accumulating conditionals, stop adding to them right now. Use every new request as an opportunity to do a bit of design. Implement the change using small, well-designed classes that collaborate with the existing object.

A 5,000 line class exerts a gravitational pull that makes it hard to imagine creating a set of 10 line helper classes to meet a new requirement. Make new classes anyway. The way to get the outliers back on the green line where they belong is to resist putting more code in objects that are already too large. Make small objects, and over time, the big ones will disappear.

Best,
Sandi

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

]]>Why We Argue: StyleSandi MetzTue, 18 Jul 2017 17:30:00 +0000https://www.sandimetz.com/blog/2017/6/1/why-we-argue-style537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:59305debe4fcb5d6a7d89018I've been thinking about why we argue about code, and how we might
transform vehement differences of opinion into active forces for good.
My thoughts spring from a very specific context. Ten or twelve times a
year I go to an arbitrary business and spend three or more days teaching a
course in object-oriented design. I'm an outsider, but for a few days
these business let me in on their secrets.
Here's what I've noticed. In some places, folks are generally happy.
Programmers get along. They feel as if they are all "in this together."
At businesses like this I spend most of my time actually teaching
object-oriented design.
Other places, folks are surprisingly miserable. ...This post originally appeared in my Chainline Newsletter.

I've been thinking about why we argue about code, and how we might transform vehement differences of opinion into active forces for good.

My thoughts spring from a very specific context. Ten or twelve times a year I go to an arbitrary business and spend three or more days teaching a course in object-oriented design. I'm an outsider, but for a few days these business let me in on their secrets.

Here's what I've noticed. In some places, folks are generally happy. Programmers get along. They feel as if they are all "in this together." At businesses like this I spend most of my time actually teaching object-oriented design.

Other places, folks are surprisingly miserable. There's a lot of discord, and the programmers have devolved into competing "camps." In these situations the course rapidly morphs away from OO Design and into wide-ranging group discussions about how to resolve deeply embedded conflicts.

Tolstoy famously said that "Happy families are all alike; every unhappy family is unhappy in its own way." This is known as the Anna Karenina Principle, and describes situations in which success depends on meeting all of a number of criteria. The only way to be happy is to succeed at every one of them. Unhappiness, unfortunately, can be achieved by any combination of failure. Thus, all happy businesses are similar, but unhappy ones appear unique in their misery.

Having now taught a fair number of classes, I've experienced various unhappy shops and am beginning to discern some common criteria for happiness. In this newsletter I'll discuss just one on them, with more to follow in future newsletters.

Today I'm interested in choices of syntax, i.e whether or not your shop has agreed upon and follows a style guide. If you're surprised that I'm starting with this apparently mundane issue, consider yourself lucky in your choice of workplace. If you're shaking your head in rueful agreement about the importance of this topic, I feel your pain.

Why Have A Style Guide?

I firmly believe that all of the code that I personally have to examine should come to me in a consistent format. Code is read many more times than it is written, which means that the ultimate cost of code is in its reading. It therefore follows that code should be optimized for readability, which in turn dictates that an application's code should all follow the same style. Adhering to a common style saves you money.

What Style Is Best?

Most programmers agree with the prior paragraph, but here's where things begin to break down. As far as I'm concerned, my personal formatting style is clearly the best. However, I'm quite sure that you feel the same. It's easy for a group of programmers to agree that all code should follow a common style, but surprisingly difficult to get them to agree on just what that common style should be.

The truth is, many stylistic choices are arbitrary, and purely a matter of personal preference. Choosing a style guide means building agreements in areas where we have strong differences of opinion about issues of little significance. It's not style that matters, but sameness of style.

Why Can't Teams Agree?

As I've already said, if you don't have a style guide, it's costing you money. However, if you can't agree on a style, the money may be least of your problems.

I have visited businesses where failure to come to agreement on this issue tore teams apart. The programmers had long since ceased verbal negotiations, choosing instead to employ change requests as an excuse to convert adjacent sections of code into their own preferred style. Code regularly flipped-flopped between competing styles. This not only made it difficult to discern actual changes in behavior, but it also infuriated the person who last touched the code the next time they looked at it.

These "style wars" are ostensibly about formatting code, but are actually about power. In mild forms, the wars create tension and cost money. In virulent forms, they poison team morale.

Don't I Deserve To Have My Own Way?

Well, no.

This is really a question of who gets to decide, and your approach to answering it lays bare the fissures in your shop. It's common to have three distinct lobbies, each with their own opinions about styling.

There's often a group of senior programmers who are convinced that they are right and that they deserve to have their own way. These folks attempt to rule by fiat, and when fiat fails, they feel empowered to ignore group agreements and employ their own stylistic preferences. After all, who's gonna fire them?

It's also common to see a group of programmers who came from another language writing Ruby in a style more suited to that other language. These programmers make choices that help them understand the code, disregarding the fact that their styling choices confound everyone else.

Finally, there are the newbies who don't yet have firm opinions about style. Because they are experimenting with all the styles, their code is characterized by inconsistency. Bless 'em, they mean well, but they're confusing everyone and need an intervention.

I've listed only three groups, but if your business has splintered in this way you're likely contending with many more variants of style. Once divisions form everyone goes their own way and you end up with as many styles as programmers. In shops like this, when I ask "Can you tell who wrote a piece of code by looking at it," everyone answers "Yes."

How Can Teams Reach Consensus?

The styling problem is actually twofold. First, everyone has to agree on a guide. Next, everyone has to follow it.

If you're in a shop with a history of conflict, it's best to outsource the guide. The community has throughly beaten the what-should-code-look-like horse; there's no reason for you to duplicate this effort. Just search for "Ruby (or language of choice) Style Guide" and chooseone.

Using an external guide allows you take advantage of the wisdom of the crowd while side-stepping internal squabbles. Most guides please and disappoint in equal measure, and so require compromises from all. Choosing an external guide means it's not personal when someone doesn't get their way.

It's not enough to just select a guide--once you do so everyone must follow it. The easiest way to enforce this is to put automatic processes in place to alert folks to violations in their own code. For Ruby, have a look at RuboCop, configured however works best for you.

Avoid appointing a human "style cop", which just forces someone to be an increasingly ill-tempered nag. Instead, supply programmers with the information they need to remedy their own transgressions. By the time a pull request is submitted, mis-stylings should long since have been put right. Pull request conversations ought to be about what code does rather than how code looks.

As a personal aside, I've been experimenting with Elm. My desire to format code like intentional Elm instead of awkward Ruby led me to install an automatic Elm formatter in my editor. I initially hated the look of the auto-reformatted code, but over the last few weeks I've transitioned from active dislike to genuine fondness. Repeated exposure to standard Elm styling gradually taught me to prefer it, proving again that normal is what you're accustomed to, and that it's easier to
act your way into a new way of thinking than vice versa.

What About Code That Already Exists?

Ignore it. You don't have to re-style all existing code, just do better from this day forward. Defer updating old code until you touch it for other reasons.

Following this strategy means that the code you most often work on will gradually take on a common style. It also means that some of your existing code might never get updated, but if you never look at it, who cares?

If you choose to re-style code that you otherwise have no need to touch, you're declaring that changing the look of this old code has more value to your business than delivering the next item on the backlog. The opportunity cost of making a purely aesthetic change includes losing the benefit of what you could have done instead. The rule-of-thumb is: Don't bother updating the styling of stable, existing code unless not doing so costs you money.

What If I Hate the New Style Guide?

If you disagree with the style guide upon which your team agrees, you have only two honorable options.

First, you can obey the guide despite your aversion. As with me in the Elm story above, this act is likely to change your thinking so that over time you come to prefer the new style. It's possible that if you follow the guide you'll begin to like it.

Alternatively, you can decide you will not obey the style guide. Making this decision demands that you leave your current job and find some other business whose guide matches your preferred style. Go there and follow that one.

Notice that both of these choices have you following a guide. This part is not optional.

The moral of this story? It's more important for all code to be formatted the same than it is for any one of us to get our own way. Commit to agreeing upon and following a style guide. And if you find that your team cannot come to an agreement, step away from this problem and start a discussion about power.

Best,Sandi

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

]]>The Half-Life of CodeSandi MetzThu, 01 Jun 2017 17:33:14 +0000https://www.sandimetz.com/blog/2017/6/1/the-half-life-of-code537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:59301a75d2b857caa399e6e6I've been thinking about the half-life of code.
In his Software that Fits in Your Head talk, Dan North defines the
half-life of software as (I'm paraphrasing) "the amount of time required
for half of an application's code to change so much that it becomes
unrecognizable."
In that talk he tells the story of working on a high quality, non-trivial
application whose code's half-life was six weeks.
Yup. Six. Weeks.
This post originally appeared in my Chainline Newsletter.

I've been thinking about the half-life of code.

In his Software that Fits in Your Head talk, Dan North defines the half-life of software as (I'm paraphrasing) "the amount of time required for half of an application's code to change so much that it becomes unrecognizable."

In that talk he tells the story of working on a high quality, non-trivial application whose code's half-life was six weeks.

Yup. Six. Weeks.

I saw that talk nearly a year ago and have been distracted by its implications ever since.

The upsides of a short code half-life are significant. Imagine how much better your life would be if your application's code always reflected the most accurate, up-to-date understanding of the problem at hand. Think about how much costs would go down if you never had to navigate dead code. Consider the value of having of an application that is free of speculative additions that were thrown in to support features that will never arrive.

I want these things, and am intrigued by the thought that taking half-life into consideration might help me achieve them.

Code is read many more times than it is written. Writing code costs something, but over time the cost of reading is often higher. Anyone who ever looks at a piece of code has to invest brain-power into figuring out what it does.

Dead code and speculative cruft make it hard to decipher the intention of code. It follows that you can reduce overall costs by optimizing code for reading rather than writing. The easiest code for subsequent readers to understand would be frugal, i.e. it would be simple, correct, and without embellishment.

I suspect, however, that I'm preaching to the choir. I suspect that you already believe everything stated in the prior paragraph. It's not that you disagree that it would be a good thing to have frugal code, it's that the applications that you work on are so far removed from this ideal that you despair of ever reaching it.

In my experience, most applications are a mess. Successful business rely on long-lived applications that endure a constant barrage of new requirements. Changes are commonly made under urgent time pressure, which drives applications towards disorder. As entropy increases, it becomes harder and harder to add features except by way of one more hack. The accumulated mess leads to hacks, hacks lead to more hacks, and then you're in a loop. Velocity gradually slows, and everyone comes to hate the application, their job, and their life.

If it makes you feel any better, there's a way in which having a big mess is a sign of success. The reason your competitors don't have messes is that they went out of business. You won, and your prize is an application that betrays the ravages of time.

And, as if that's not alarming enough, I fear that the coding culture that led to your current success may be dooming you to future failure. If your existing application impedes change, nothing good will come of doing more of what you've done. If this is the state you're in, it's time to change how you write code.

Here's where the concept of half-life matters. You can lower your costs by reducing the half-life of your least stable code.

The parts of your applications that change the most also cost the most. These dynamic parts are often fundamental to your business. They are the result of lengthy, evolutionary development, and suffer from never ending churn. You depend on them the most, yet they are hard to understand, and only getting worse.

Time is in appallingly short supply. The good news, however, is that you're not obligated to fix things that aren't costing you money. The most efficient way to improve long-lived applications is to focus on the code that churns--this is where your efforts most pay off. For maximum effect, commit to crafting solutions that are both frugal and easily replaceable.

The last item above is key. Code that isn't easy to replace doesn't get replaced, instead it gets expanded. Its conditionals get bigger. The number of class names it knows about grows larger. These sorts of expansions tightly couple the code you're changing to other parts of your application. This coupling makes it difficult to swap in alternative implementations, which it turn leads to a long half-life for the code.

Unstable code that has a long half-life inevitably accumulates cruft. This complicates the code, and programmers hesitate to neaten what they don't understand. The trick to maintaining frugality over the course of many changes is to insist on code that's easily replaceable. Achieving replaceable code necessitates developing a culture that values polymorphic objects and loosely-coupled code.

You have a bargain with other programmers about how you will write code. Your current application is this bargain made manifest. If you're finding that the original pact has outlived its usefulness, the first step to improving your life is to start talking to one another about how you wish you were writing code. Dan's talk, and the idea of the half-life of code, can spur this discussion.

Start today. :-)

Thanks for reading,

Sandi

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

]]>Make Everything The SameSandi MetzThu, 09 Jun 2016 12:23:26 +0000https://www.sandimetz.com/blog/2016/6/9/make-everything-the-same537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:57595fc12b8ddefb064716e4As part of my local ruby meetup (#westendruby), I've been dabbling in katas
and quizzes. Having worked several, I can't help but notice that my
solutions are sometimes radically different from the others.
Hmmm.This post originally appeared in my Chainline Newsletter. Due to popular request, I'm re-publishing it here on my blog. It has been lightly edited.

As part of my local ruby meetup (#westendruby), I've been dabbling in katas and quizzes. Having worked several, I can't help but notice that my solutions are sometimes radically different from the others.

Hmmm.

Having one's solutions differ from the crowd's is (and ought to be) a cause for heightened scrutiny. Therefore, I've been pondering code, trying to understand what's driving my choices. I have a glimmer of an idea, and thus, this newsletter.

The Setup

The easiest way for me to explain is for you to first go do the Roman numerals kata. Happily, there's a Roman numerals test on exercism to get you started. The task is to convert Arabic numbers into Roman numerals, and the tests are all some form of:

assert_equal 'I', 1.to_roman

or

assert_equal 'CMXI', 911.to_roman

In case your life is such that you can't drop everything and do that exercise right now, here's a reminder of how the Roman counting system works. There are two main ideas. First, a few specific numbers are represented by letters of the Roman alphabet. Next, these letters can be combined to represent other numbers.

Seven Roman letters are used. The letters and their associated values are:

I = 1

V = 5

X = 10

L = 50

C = 100

D = 500

M = 1,000

1-10 in the Arabic numbering system is I, II, III, IV, V, VI, VII, VIII, IX, and X in Roman numerals. As you may already know, there are two rules at work.

1, 2 and 3 illustrate the first rule. 1 is one I. 2 is two Is. 3 is three Is. The rule is: for Arabic value x, select the largest Roman letter that's less than x, and generate x number of copies. Let's call this the 'additive' rule.

4 follows the second rule. Instead of IIII (four I's), 4 is written as IV (one less than five). This rule is: select the first Roman letter higher than the Arabic value, and prefix it with the adjacent lower letter. This rule is used in cases where 4 sequential occurrences of any letter would otherwise be necessary. Thus, 4 is IV instead of IIII, 9 is IX instead of VIIII, etc. Let's call this the 'subtractive' rule.

Now, consider the code needed to satisfy this kata. Given that there are two rules, it seems as if there must be two cases. To handle the two cases, it feels like the code will need a conditional that has two branches, one for each rule.

The actual implementation code might be more procedural (the conversion logic could be hard-coded into the branches of the conditional) or more object-oriented (you could create an object to handle each rule and have a conditional somewhere to select the correct one), but regardless of whether you write a procedure or use OO composition, there's still a conditional.

The Insight

I hated this. Not only did I not want the conditional, but figuring out when to use which rule seemed like a royal PITA. It felt like the conditional would need to do something clever to select the correct rule, and I wasn't feeling particularly quick-witted. Thus, I found myself pondering this kata with a faint sense of dread, while the meetup loomed.

Regardless, I sat down to write some code, and immediately realized that although I was faintly aware that there were two conversion rules, I didn't know the full set of Roman letters and their associated Arabic values. I then consulted the wikipedia page for Roman numerals, where I found something which gave me a dramatically simpler view the problem. Serious lightbulb moment.

It turns out that the way we think about Roman numerals today is the result of an evolutionary process. In the beginning, they were uniformly additive. 4 was written as IIII and 9, VIIII. As time passed, the subtractive form crept in. 4 became IV, and 9, IX. In modern times we consistently use the shorter, subtractive form, but in Roman times it was common to see one form, or the other, or a combination thereof.

This means that the additive form is a completely legitimate kind of Roman numeral. (Who knew?) It can be produced in its entirety by rule 1, which is comfortingly simple to implement. The conversion from additive to subtractive is also dead easy, and can be accomplished via a simple mapping that encodes rule 2.

The key insight here is that converting from Arabic to additive Roman is one idea, and converting from additive to subtractive Roman is quite another. Solving this kata by converting Arabic numbers directly into subtractive Roman skips a step, and conflates these two ideas. It is this conflation that dooms us to the conditional.

Having had this realization, I wrote two simple bits of code. One converted Arabic to additive Roman, the other additive to subtractive Roman. Used in combination, they pass the tests.

I took the code to #westendruby, where someone pointed out that not only was my variant more understandable than many other implementations, but also that it could easily be extended to perform the reverse conversion. They were absolutely right; it took just a few lines of additional code to convert from Roman numerals back into Arabic numbers. Adding this new feature to other implementations was far more difficult.

I wrote several versions of the kata. Here's the one I ended up liking the best.

The Upshot

I left that meetup with a newfound respect for what it means to have a conditional.

Conditionals are trying to tell you something. Sometimes it is that you ought to be using composition, i.e., that you should create multiple objects that play a common role, and then select and inject one of these objects for use in place of the conditional. Composition is the right solution when a single abstract concept has several concrete implementations.

However, rule 1 and rule 2 above don't represent alternative implementations of the same concept, instead they represent two entirely unrelated ideas. The solution here is therefore not composition, but instead to create two transformations, and apply them in order. This lets you replace one "special" case with two normal ones, and reap the following benefits:

The resulting code is more straightforward.

The tests are more understandable.

The code can produce the pure additive form of Roman numerals, in addition to the subtractive one.

The code is easily extended to do the reverse conversion.

The keystone in this arch of understanding is being comfortable with transformations that appear to do nothing. It is entirely possible for a Roman numeral to look identical in its additive and subtractive forms. III for example, looks the same either way. Regardless, the additive III must be permitted to pass unhindered through the transformation to subtractive. You can't check to see if it needs to be converted, instead you must blithely convert it. This makes everything the same, and it is sameness that gets rid of the conditional.

The Commentary

Now, if you'll permit, I'll speculate. I'm interested in why this solution occurred to me, but not others. Folks at the meetup found it startling in its simplicity and utility. Once known, it seems inevitable, but before knowing, inconceivable.

What would someone have to know in order to be able to dream up this solution? How can we teach OO so that folks learn to look at similar problems and recognize the underlying concepts? What quality in my background or style of thinking revealed them to me? Mind you, I'm not saying that my solution is perfect, but it's certainly different. Why?

I think there are two reasons. First, I'm committed to simplicity. I believe in it, and insist upon it. I am unwilling to settle for complexity. Simplicity is often harder then complexity, but it's worth the struggle, and everything in my experience tells me that with enough effort, it's achievable. I have faith.

Next, the desire for simplicity means that I abhor special cases. I am willing to trade CPU cycles to achieve sameness. I'll happily perform unnecessary operations on objects that are already perfectly okay if that lets me treat them interchangeably. Code is read many more times that it is written, and computers are fast. This trade is a bargain that I'll take every time.

So:
Insist on simplicity.
Resist special cases.
Listen to conditionals.
Identify underlying concepts.
And search for the abstractions that let you treat everything the same.

Thanks for reading,

Sandi

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

]]>The Wrong AbstractionSandi MetzWed, 20 Jan 2016 20:30:00 +0000https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:5698f9f55a5668e0742681faI've been thinking about the consequences of the "wrong abstraction." My
RailsConf 2014 "all the little things" talk included a section where I
asserted:
> duplication is far cheaper than the wrong abstraction
And in the summary, I went on to advise:
> prefer duplication over the wrong abstraction
This small section of a much bigger talk invoked a surprisingly strong
reaction. A few folks suggested that I had lost my mind, but many more
expressed sentiments along the lines of _I originally wrote the following for my Chainline Newsletter, but I continue to get tweets about this idea, so I'm re-publishing the article here on my blog. This version has been lightly edited._

The strength of the reaction made me realize just how widespread and intractable the "wrong abstraction" problem is. I started asking questions and came to see the following pattern:

Programmer A sees duplication.

Programmer A extracts duplication and gives it a name.

This creates a new abstraction. It could be a new method, or perhaps even a new class.

Programmer A replaces the duplication with the new abstraction.

Ah, the code is perfect. Programmer A trots happily away.

Time passes.

A new requirement appears for which the current abstraction is almost perfect.

Programmer B gets tasked to implement this requirement.

Programmer B feels honor-bound to retain the existing abstraction, but since isn't exactly the same for every case, they alter the code to take a parameter, and then add logic to conditionally do the right thing based on the value of that parameter.

What was once a universal abstraction now behaves differently for different cases.

Another new requirement arrives.Programmer X.Another additional parameter.Another new conditional.Loop until code becomes incomprehensible.

You appear in the story about here, and your life takes a dramatic turn for the worse.

Existing code exerts a powerful influence. Its very presence argues that it is both correct and necessary. We know that code represents effort expended, and we are very motivated to preserve the value of this effort. And, unfortunately, the sad truth is that the more complicated and incomprehensible the code, i.e. the deeper the investment in creating it, the more we feel pressure to retain it (the "sunk cost fallacy"). It's as if our unconscious tell us "Goodness, that's so confusing, it must have taken ages to get right. Surely it's really, really important. It would be a sin to let all that effort go to waste."

When you appear in this story in step 8 above, this pressure may compel you to proceed forward, that is, to implement the new requirement by changing the existing code. Attempting to do so, however, is brutal. The code no longer represents a single, common abstraction, but has instead become a condition-laden procedure which interleaves a number of vaguely associated ideas. It is hard to understand and easy to break.

If you find yourself in this situation, resist being driven by sunk costs. When dealing with the wrong abstraction, the fastest way forward is back. Do the following:

Re-introduce duplication by inlining the abstracted code back into every caller.

Within each caller, use the parameters being passed to determine the subset of the inlined code that this specific caller executes.

Delete the bits that aren't needed for this particular caller.

This removes both the abstraction and the conditionals, and reduces each caller to only the code it needs. When you rewind decisions in this way, it's common to find that although each caller ostensibly invoked a shared abstraction, the code they were running was fairly unique. Once you completely remove the old abstraction you can start anew, re-isolating duplication and re-extracting abstractions.

I've seen problems where folks were trying valiantly to move forward with the wrong abstraction, but having very little success. Adding new features was incredibly hard, and each success further complicated the code, which made adding the next feature even harder. When they altered their point of view from "I must preserve our investment in this code" to "This code made sense for a while, but perhaps we've learned all we can from it," and gave themselves permission to re-think their abstractions in light of current requirements, everything got easier. Once they inlined the code, the path forward became obvious, and adding new features become faster and easier.

The moral of this story? Don't get trapped by the sunk cost fallacy. If you find yourself passing parameters and adding conditional paths through shared code, the abstraction is incorrect. It may have been right to begin with, but that day has passed. Once an abstraction is proved wrong the best strategy is to re-introduce duplication and let it show you what's right. Although it occasionally makes sense to accumulate a few conditionals to gain insight into what's going on, you'll suffer less pain if you abandon the wrong abstraction sooner rather than later.

When the abstraction is wrong, the fastest way forward is back. This is not retreat, it's advance in a better direction. Do it. You'll improve your own life, and the lives of all who follow.

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

POODNYC 2015 Scholarships have been awardedSandi MetzWed, 26 Aug 2015 12:00:00 +0000https://www.sandimetz.com/blog/2015/8/24/poodnyc-2015-scholarships-have-been-awarded537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:55cf3773e4b0ebe2dfee0f6bScholarships for the Oct 19-21 Practical Object-Oriented Design Course
(POODNYC) in New York City have been awarded! Winners are listed below,
but before I introduce them I'd like to give an overview of the applicant
pool and selection process.Scholarships for the Oct 19-21 Practical Object-Oriented Design Course (POODNYC) in New York City have been awarded! Winners are listed below, but before I introduce them I'd like to give an overview of the applicant pool and selection process.

I'll be awarding scholarships for future public classes and hope that transparency about how this works will motivate you into talking some deserving person into applying, or into applying for your own deserving self.

The POODNYC Scholarship

The scholarship includes a seat in POODNYC, and airfare to and lodging in NYC (all courtesy of Hashrocket, to whom I am very grateful). As you can see, it's a full ride. The intent was to remove every financial barrier that would prevent the recipient from attending.

Applicants

There were 24 applicants.

Experience Level:

16 - less than 1 year of experience or currently in school / attending bootcamp

8 - 1+ years

8 - career changers

Gender:

19 - women

5 - men

Minorities/Under Represented:

13 - people of color (4 men, 9 women)

3 - women over 35

Location:

11 - New York

7 - Other states

6 - International (Ecuador, England and Germany)

Selection Criteria

We (me and 2 others) knew that we wanted these scholarships to support good works and/or diversity. The first time we gave scholarships (for the Oct 2014 POODNC course) we supplied very minimal instructions and relied on each person to argue their best case. These instructions merely said 'Tell us why you deserve a scholarship'. This year we added a additional field for ‘Amount of Programming Experience’; more on this below.

While we don't have a rigid checklist by which to evaluate applications, we definitely look favorably upon candidates who:

are engaged in good works

have a moderate amount of programming experience

are demographically diverse from the community at large

have a clear financial need

Good Works:

We preferred candidates with a demonstrated track record of good works, where we define 'good work' as anything from "I spend my spare time on 'Code for America' projects" to "I volunteer as a coach at RailsBridge, RailsGirls, BlackGirlsCode". We preferred candidates who could say "This scholarship will help me do a better job at this thing I am already doing" over candidates who said "This scholarship will make me better".

This year there were so many applicants giving back to the community that we gave additional weight to this criteria.

Experience:

We required some amount of real-world programming experience, 'some' being defined as 'more than a bootcamp'. Folks with very little programming experience have successfully taken this course, but more experienced programmers get correspondingly more out of it. The scholarships are intended as levers to support change; requiring at least 6 months (ish) of real-world programming experience moves the fulcrum and makes each scholarship have more value.

There were a number of applicants who, although they were engaged in all kinds of good works, didn't yet have enough experience to qualify for a scholarship. We regretfully removed them from consideration and urge them to reapply in the future.

Diversity:

We believe that human diversity improves both the software we create and the community in which we work. We were biased towards candidates who differed from the demographic norm (i.e., in age, ethnicity, gender, etc).

Financial need:

While last year we required folks to demostrate a clear financial need, this year a few candidates were engaged in such impressive good works that we were tempted to ignore this criteria. As a result of this experience, we are officially softening our stance on financial need. Although we will continue to take ability to pay into consideration, future scholarship applicants will not be disqualified based on ability to pay.

This year we did not disqualify a single candidate based on our assemement of their ability to pay.

As I said above, we didn't explicitly ask for financial, experience, demographic or good works information but it was easy to get. Many applicants actually included it on their submission and simple web searches unearthed the missing bits.

Evaluation

A 35-something woman of color who was in the midst of career transition while organizing a community meetup and hosting hack-a-thons would rank very high by the criteria above, and a 20-something Caucasian male who was employed as a junior developer, well, not so much.

The applicants for POODNYC were doing so much good for the community that we added ‘Good Works’ to ‘Experience Level’ in order to stay in the running. Applications which survived those tests went on to be evaluated based on ‘Diversity’ and ‘Financial Need’. We narrowed the list from 24 to five finalists (interestingly, like last year, the demographics of the five matched those of the whole), and then selected the two winners.

The Winners Are

Charlotte Chang

Charlotte is a career changer and a recent programming convert. Upon graduating from Flatiron School, she tried freelancing only to discover that "it takes a village to raise a junior developer". She lives in Cleveland, a former industrial powerhouse which has lost 50% of its manufacturing jobs since 1954, and which had a median household income of $26,096 in 2013. Living in a community which needs to shift its focus to building technology skills led Charlotte to give back.

Charlotte doesn't want to be just a 'developer', she wants to be a great developer, one with strong coding practices who gives back to the community and becomes a mentor for others. Charlotte currently works at LeanDog.

* No, she did not receive a scholarship because she volunteers for the bike path. This is pure coincidence.

Richard Lau

Richard is a combat veteran who is passionate about helping other veterans transition their career after their service is complete. He is currently building a free program to help veterans, along with their spouses and children, learn programming. Richard is also an advocate for veteran entrepreneurship.

He attended the Web Development Immersive at General Assemb.ly on a Veteran Scholarship. After graduating from the program he started volunteering for RailsBridge NYC. Richard plans to use the course to improve his skills so he can be a better and more knowledgable teacher. He also serves as a co-organizer of the New York VIM meetup.

Richard lives in New York City.

We Want You

As you can see, a candidate who is engaged in good works and is an outlier in every demographic category would be unbeatable, but qualifying on even a subset of these criteria can win you a scholarship. If you, or someone you know, fills the bill, I hope to see your application in the future.

A number of applicants were actively doing good works but did not yet have enough experience to get the most out of the course. If you're one of these folks, I urge you to re-apply in the future. One of today's winners is just such a second time applicant, and proof that additional experience combined with persistence can pay off.

So there you have it ... the POODNYC scholarship winners. Please join me in extending my congratulations to Richard and Charlotte. Our community is improved by their presence. I'm grateful that they're here and gratified to support them along their way.

In closing, one more shout-out to Hashrocket. Their continuing support of POOD course scholarships is a sign of their ongoing commitment to our community and reflects their core values. My thanks.

Sign up
for my newsletter, which contains random thoughts that don't quite make it into blog posts.

]]>Suspicions of nil Sandi MetzTue, 23 Dec 2014 03:30:00 +0000https://www.sandimetz.com/blog/2014/12/19/suspicions-of-nil537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:54940debe4b0f2afdc9570d6I'm feeling suspicious of nil.
What is nil?
In a recent newsletter I pondered true and false, and suggested that
thinking of them as normal, everyday objects could expand your ideas about
OOP. Today I'll continue with this theme by considering nil.
What _is_ nil?
A straightforward, very concrete definition might be that it's an instance
of NilClass and a specialized kind of Ruby singleton that responds to 70+
public/protected methods.
Let's have a look...I'm feeling suspicious of nil.

What is nil?

In a recent newsletter I pondered true and false and suggested that thinking of them as normal, everyday objects could expand your ideas about OOP. Today I'll continue with this theme by considering nil.

What isnil?

A straightforward, very concrete definition might be that it's an instance of NilClass and a specialized kind of Ruby singleton that responds to 70+ public/protected methods.

In Ruby, nil (and also true and false) are defined in a slightly special way, such that their class and their singleton class are the same. As someone who's not all that caught up in the gory internal details, I generally ignore this.

So, nil knows some boolean operations and it can convert itself into a few other things. As nil is already defined when I start Ruby, clearly Ruby itself created this instance of NilClass and stored it in nil for my use.

Using nil to mean 'no result'

So far this has been fairly mundane. NilClass is clearly a class and nil is the one and only instance of that class; there's no magic here. Even if you thought of nil as 'special' when you started this post, by now you should be quite disabused of that notion. nil is a regular old object that responds to its own set of messages, just like any other.

However, despite this apparent normalcy, we create problems for ourselves because it's so easy to treat nil as something other than a straightforward object. For example, it's common to use nil to signal absence of a result to the senders of messages. As a matter of fact, one might defensibly argue that this is part of nil's purpose.

Notice that Animal.find normally returns an instance of Animal, but when it receives an empty string for id it returns nil. This implementation means that if an innocent bystander gets passed a list of ids...

The proximate cause of this error is clearly that nil doesn't understand name, but what really happened?

The underlying OO problem is that Animal.find returns instances of two different types (Animal or NilClass) and these types conform to different API's.

Let me repeat that.

The underlying problem is that Animal.find returns objects which conform to different API's.

This has deep consequences. Senders of Animal.find:

are forced to examine the type of the result in order to know how it behaves, and then

must write code that in effect says, "Well, if I get back this type of thing then I'll supply the proper behavior, but if I get back anything else I'll just expect whatever I got back to respond to name.

Senders of Animal.find incur a deep and pervasive burden when they cannot trust returned objects to respond to a common set of messages.

Which leads us to...

Who should supply the correct behavior?

If you plan to send name to whatever you get back, and you get back nil instead of an Animal, you are forced to explicitly check for nil and supply the correct behavior yourself.

There are many ways to do this and, frankly, most don't seem horrible. One very straightforward option is to send the nil? message to the value held in animal and, based on the result, choose between returning the correct response or sending the name message. Here's an implementation that returns the string 'no animal' when the result is nil:

Alternatively, you can be less verbose and rely on nil's falsy-ness. The following implementation returns an empty string when there's no animal:

animals.each {|animal| puts animal && animal.name}
=>
pig
sheep

RubyOnRails mitigates the repetition in the previous example by monkey patching Object and NilClass with the try method. If you're using Rails (or if you were to add the patch yourself) you can reduce the last example to:

animals.each {|animal| puts animal.try(:name)}
=>
pig
sheep

But try, handy as it may be, is an enabler. Using it reflexively encourages a kind of thinking that is detrimental to your understanding of objects.

Please understand, I'm not suggesting that you never use try or that there's never a good reason to return nil, I just want you to be conscious of the bargain you're making when you do. Workarounds like try make it easy to ignore the fact that you're invoking methods that return untrustworthy results and that your code is forcing the senders of messages to check the type of the return and then supply missing behavior.

If you send Animal.find from more than one place in your application and you expect to be able to send name to whatever you get back, you are now doomed to duplicate this missing behavior in every one of those places. No amount of making the code that supplies the missing behavior 'easy to write' addresses the underlying problem.

To my mind, the following variants are dang near identical and equally troublesome. If you object to the last, you should be bothered by the first.

Each example above puts the burden of knowing how to handle two unrelated return types on senders of Animal.find.

We've come to blithely accept this when one of the two return types is NilClass, but I'll bet you'd complain mightily about an API that most times returned an Animal but, occasionally, if it couldn't find a suitable Animal, chose instead to return a Carrot. Carrots are orange and crunchy and my Guinea Pigs love them but they in no way behave like animals. A substitution of this sort has downstream consequences for everyone.

In flexible, forgiving, easily maintainable OO apps the objects returned from message sends conform to a common API; they behave exactly as you expect. It's one thing to return nil to signal 'no result' and it's quite another to return nil to callers who expect it to respond to messages it can't possibly understand.

I completely concede that returning nil can be handy and agree, in advance, that it's sometimes exactly right. I'm not suggesting that you never do this; I do it myself. However, even when nil is the right thing to return, we want to avoid forcing many places in our application to supply the same 'alternative name' behavior.

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

]]>POODNC Scholarships Have Been AwardedSandi MetzMon, 15 Sep 2014 15:15:00 +0000https://www.sandimetz.com/blog/2014/9/15/scholarships-have-been-awarded537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:54160c44e4b06505bdb08836Scholarships for the Oct 29-31 Practical Object-Oriented Design Course
(POODNC) in Durham, NC have been awarded! Winners are listed below, but
before I introduce them I'd like to give an overview of the applicant pool
and selection process.
I'll be awarding scholarships for future public classes and hope that
transparency about how this works will motivate you to talk some deserving
person into applying, or to apply for your own deserving self.
The POODNC Scholarship
The scholarship includes a seat in the POODNC course
(courtesy of me), and airfare and lodging (courtesy of Hashrocket, to
whom I am very grateful). As you can see, it's a full ride. The intent was
to remove every financial barrier that would prevent the recipient from
attending.Scholarships for the
Oct 29-31 Practical Object-Oriented Design Course (POODNC) in Durham, NC have been awarded! Winners are listed below, but before I introduce them I'd like to give an overview of the applicant pool and selection process.

I'll be awarding scholarships for future public classes and hope that transparency about how this works will motivate you to talk some deserving person into applying, or to apply for your own deserving self.

The POODNC Scholarship

The scholarship includes a seat in the
POODNC course
(courtesy of me), and airfare and lodging (courtesy of Hashrocket, to whom I am very grateful). As you can see, it's a full ride. The intent was to remove every financial barrier that would prevent the recipient from attending.

Applicants

There were 44 applicants.

Experience Level:

6 - currently in school / attending bootcamp

30 - 6+ months

8 - career changers

Gender:

28 - women

16 - men

Minorities/Under Represented:

19 - people of color (10 men, 9 women)

3 - women over 35

Selection Criteria

We (me and 2 others) knew that we wanted these scholarships to support good works and/or diversity, but weren't initially sure how we'd ultimately select the winners. We decided to give very minimal application instructions and rely on each person to argue their best case. The instructions merely said 'Tell us why you deserve a scholarship'.

As we evaluated the applications our priorities became clear and definite selection criteria evolved. Ultimately, we looked favorably upon candidates who:

had a clear financial need

had a moderate amount of programming experience

were demographically diverse from the community at large

were engaged in good works

Financial need:

We were biased towards folks who were underemployed and away from folks who appeared to have reasonably well-paying jobs. This bias held even for folks who were attempting to change from non-programming to programming careers. We removed from consideration those whom we regarded as 'able to pay'.

Experience:

We required some amount of real-world programming experience, 'some' being defined as 'more than a bootcamp'. Folks with very little programming experience have successfully taken this course, but more experienced programmers get correspondingly more out of it. The scholarships are intended as levers to support change; requiring 6 months (ish) of real-world programming experience moves the fulcrum and makes each scholarship have more value. We removed from consideration those who did not have the minimum amount of experience.

Diversity:

We believe that human diversity improves both the software we create and the community in which we work. We were biased towards candidates who differed from the demographic norm (i.e., in age, ethnicity, gender, etc). This quality tied in importance with 'Good Works'.

Good Works:

We preferred candidates with a demonstrated track record of good works, where we define 'good work' as anything from "I spend my spare time on 'Code for America' projects" to "I volunteer as a coach at RailsBridge, RailsGirls, BlackGirlsCode". We preferred candidates who could say "This scholarship will help me do a better job at this thing I am already doing" over candidates who said "This scholarship will make me better". This quality tied in importance with 'Diversity'.

As I said, we didn't explicitly ask for financial, experience, demographic or good works information but it was easy to get. Many applicants actually included it on their submission and simple web searches unearthed the missing bits.

Evaluation

A 40-something, Hispanic male who worked in retail, taught himself programming and had been contributing to Code For America for several years would rank very high by the criteria above, and a 20-something, Caucasian female who was currently employed as an accountant but wanted to switch to programming, well, not so much.

The 'Financial Need' and 'Experience Level' criteria had to be met in order to stay in the running; applications which survived those tests went on to be evaluated based on 'Good Works' and 'Diversity'. We winnowed the list from 44 applicants to nine finalists (interestingly enough, the demographics of these nine matched the demographics of the whole), and then selected the two winners.

The Winners Are

LaToya Allen

LaToya is passionate about teaching students from under-represented communities how to code. She’s taught Ruby, Javascript and TDD to girls from an under-represented high school and has volunteered with Girl Develop It and Chicago Women Developers to teach women and girls who, like herself, are self taught.

She'll use what she learns to continue teaching programming to students in after-school programs. Her goal is to help kids who have little or no hope of going to college learn skill-sets that will lift them out of poverty, giving them a life they may not be able to envision.

She is currently a Resident Apprentice at 8th Light in Chicago.

Omowale Oniyide

Omowale's recent discovery of programming changed her world. She returned to school this year to pursue a degree in game development and has already submitted a physical prototype of her first game to IndieCade, the International Festival of Independent Games.

She's currently working with a Rails Girls team to build an in-browser REPL for the Ruby Standard Library, with the goal of making it more accessible to all (think TryRuby.org). The team recently presented at the LA Ruby Meetup where they discussed their use of mob and pair programming and they're about to speak at Rocky Mountain Ruby, where they'll demo their initial work. They hope to inspire others to join their efforts, both on this project and with Rails Girls in general.

Omowale is a student game developer from Los Angeles, CA, who focuses on blending play between physical and digital spaces.

We Want You

As you can see, a candidate who is engaged in good works and is an outlier in every demographic category would be unbeatable, but qualifying on even a subset of these criteria can win you a scholarship. If you, or someone you know, fills the bill, I hope to see your application in the future.

Meanwhile, please join with me in extending my congratulations to LaToya and Omowale. They're gonna succeed in whatever they choose and our community is improved by their presence. I'm grateful that they're here and gratified to support them along their way.

And finally ... you don't actually need to win a scholarship, you can get into the course the old-fashioned way by simply purchasing a ticket.

Sign up for my newsletter, which contains random thoughts that never make it into blog posts.

]]>The Shape at the Bottom of All ThingsSandi MetzTue, 09 Sep 2014 11:00:00 +0000https://www.sandimetz.com/blog/2014/9/9/shape-at-the-bottom-of-all-things537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:53f9e527e4b06aa2bfc5449dI've been teaching a fair amount, which means I've been revisiting my
'class problems' regularly. When I chose the problems, I thought that I
understood them completely (hubris, I know) but now that I've worked them
repeatedly I'm seeing new and surprising things.
These new things have to do with the _shape_ of code. Code can be written,
or shaped, in many ways, and I've always believed that for any given
problem many different code shapes gave equally 'good' solutions. (I think
of programming as an art and am willing to give artists a fair amount of
expressive leeway.)
But I'm having a change of heart.I've been
teaching
a fair amount, which means I've been revisiting my 'class problems' regularly. When I chose the problems, I thought that I understood them completely (hubris, I know) but now that I've worked them repeatedly I'm seeing new and surprising things.

These new things have to do with the shape of code. Code can be written, or shaped, in many ways, and I've always believed that for any given problem many different code shapes gave equally 'good' solutions. (I think of programming as an art and am willing to give artists a fair amount of expressive leeway.)

But I'm having a change of heart. These days it feels like all shapes are not equally 'good', that some code shapes are actually better than others. Some shapes expose information that others conceal. This blog post illustrates the transition I'm undergoing.

Example 1 below is a slightly modified version of the code [1] used in my previous blog post Getting It Right by Betting on Wrong about the
Open/Closed principle. The House class contains code to produce the tale 'The House that Jack Built' [2]. The Controller class invokes House#line in its #play_house method on line 33. Line 37 invokes the controller. The output is on line 40.

###Example 1: The House that Jack Built

class House
DATA = [
'the horse and the hound and the horn that belonged to',
'the farmer sowing his corn that kept',
'the rooster that crowed in the morn that woke',
'the priest all shaven and shorn that married',
'the man all tattered and torn that kissed',
'the maiden all forlorn that milked',
'the cow with the crumpled horn that tossed',
'the dog that worried',
'the cat that killed',
'the rat that ate',
'the malt that lay in',
'the house that Jack built',
]
def recite
(1..DATA.length).map {|i| line(i)}.join("\n")
end
def line(number)
"This is #{phrase(number)}.\n"
end
private
def phrase(number)
DATA.last(number).join(" ")
end
end
class Controller
def play_house
House.new.line(12)
end
end
puts "\n----\n" + Controller.new.play_house
# ----
# This is the horse and the hound and the horn that belonged to the farmer sowing his corn that kept the rooster that crowed in the morn that woke the priest all shaven and shorn that married the man all tattered and torn that kissed the maiden all forlorn that milked the cow with the crumpled horn that tossed the dog that worried the cat that killed the rat that ate the malt that lay in the house that Jack built.

Example 1 works fine but let's imagine that requirements change. Our customer tells us that they like House and they want it to continue to work as is, but they'd also like a variant that randomizes the data before producing the tale.

Example 2 meets this new requirement. House#initialize now takes random, a boolean. If random is false, House behaves normally, if true, House randomizes and caches the data before producing the tale.

###Example 2

class House
# ...
def initialize(random)
@pieces = DATA.shuffle if random
end
def recite
(1..pieces.length).map {|i| line(i)}.join("\n")
end
def line(number)
"This is #{phrase(number)}.\n"
end
private
def phrase(number)
pieces.last(number).join(" ")
end
def pieces
@pieces ||= DATA
end
end
class Controller
def play_house(random = false)
House.new(random).line(12)
end
end
puts "\n--random? false--\n" + Controller.new.play_house(false)
puts "\n--random? true --\n" + Controller.new.play_house(true)
# --random? false--
# This is the horse and the hound and the horn that belonged to the farmer sowing his corn that kept the rooster that crowed in the morn that woke the priest all shaven and shorn that married the man all tattered and torn that kissed the maiden all forlorn that milked the cow with the crumpled horn that tossed the dog that worried the cat that killed the rat that ate the malt that lay in the house that Jack built.
# --random? true --
# This is the rat that ate the malt that lay in the priest all shaven and shorn that married the farmer sowing his corn that kept the cat that killed the house that Jack built the horse and the hound and the horn that belonged to the man all tattered and torn that kissed the cow with the crumpled horn that tossed the maiden all forlorn that milked the dog that worried the rooster that crowed in the morn that woke.

Example 2 now contains conditionals on line 4 and 21. These conditionals collaborate to meet the 'random' requirement but the way the code is shaped makes it hard to see that these two conditionals are about the same concept. Not only are they far apart in the code but one is expressed as a trailing if (which checks the value of random) and the other as ||= (which checks the value of @pieces).

Changing the requirements again will bring the underlying issue more sharply into focus. Our customer, when shown the output, decides they'd like a third variant. The current 'randomized' version can end in very unsatisfying ways (for example, with 'the rat that ate'). Our customer would like a 'mostly random' version which randomizes all pieces except the last. This 'mostly random' version should always end with 'the house that Jack built'.

Example 3 shows the interesting new bits of code.

###Example 3

class House
# ...
attr_reader :pieces
def initialize(order)
@pieces = initialize_pieces(order)
end
# ...
def initialize_pieces(order)
case order
when :random
DATA.shuffle
when :mostly_random
DATA[0...-1].shuffle << DATA.last
else
DATA
end
end
end
class Controller
def play_house(choice = nil)
House.new(choice).line(12)
end
end
puts "\n----\n" + Controller.new.play_house
puts "\n--:random--\n" + Controller.new.play_house(:random)
puts "\n--:mostly_random--\n" + Controller.new.play_house(:mostly_random)
# ----
# This is the horse and the hound and the horn that belonged to the farmer sowing his corn that kept the rooster that crowed in the morn that woke the priest all shaven and shorn that married the man all tattered and torn that kissed the maiden all forlorn that milked the cow with the crumpled horn that tossed the dog that worried the cat that killed the rat that ate the malt that lay in the house that Jack built.
# --:random--
# This is the dog that worried the house that Jack built the malt that lay in the rat that ate the maiden all forlorn that milked the cat that killed the rooster that crowed in the morn that woke the horse and the hound and the horn that belonged to the man all tattered and torn that kissed the farmer sowing his corn that kept the priest all shaven and shorn that married the cow with the crumpled horn that tossed.
# --:mostly_random--
# This is the man all tattered and torn that kissed the cow with the crumpled horn that tossed the maiden all forlorn that milked the horse and the hound and the horn that belonged to the dog that worried the malt that lay in the rooster that crowed in the morn that woke the rat that ate the cat that killed the farmer sowing his corn that kept the priest all shaven and shorn that married the house that Jack built.

Now that we have three different ordering requirements it's no longer sufficient to pass a boolean. Therefore, House's initialize method takes a symbol (:random, :mostly_random or anything else) and sets the value of @pieces to the result of calling initialize_pieces on that symbol (line 6 above). #initialize_pieces contains a case statement (lines 11-18) that arranges the data in the correct order and returns it.

Example 3 does two new things. First, it adds the new 'mostly random' variant, and second, it moves all of the code related to the concept of 'data order' into a single case statement.

While we certainly need to do the first we could easily have gotten by without the second. We could instead have kept the existing #pieces method and omitted the else branch from the new case statement, like so:

def initialize_pieces(order)
case order
when :random
DATA.shuffle
when :mostly_random
DATA[0...-1].shuffle << DATA.last
end
end
def pieces
@pieces ||= DATA
end

This works, but the code doesn't feel natural. Once the number of variants forces us to change to a case statement it feels more 'right' to expect that case statement to deal with all of the ordering, including the default. The code above separates the default from the variants while Example 3 treats the default as a variant. Example 3 line 17 replaces Example 2 line 21 and groups all of the code that controls the concept of 'order' in one place.

The key idea here is that 'not changing the order' is a real thing, as real as 'randomizing' or 'mostly randomizing' it. It's not as if :random and :mostly_random represent one concept and 'doing nothing' represents another. There's one concept, 'order', and a number of different possibilities. One way to order something is to leave its current order unchanged; this is an algorithm as valid as any other.

Now that we're treating every order as a real thing let's do a thought exercise. Imagine that each branch of the case statement contained many lines of code, so much that you felt obliged to extract them into methods of their own. How would you name these extracted methods?

These xxx_order methods above represent 'order' variants. Unsurprisingly, most of the method names reflect the symbols that we used in the case statement. Symbol :random becomes method #random_order, :mostly_random becomes #mostly_random__order and the else branch becomes #default_order. The fact that we can imagine a method named #default_order supports the notion that the else branch represents the same kind of thing as the other branches. Ordering something as 'unchanged' is as valid as ordering it 'random'; to insist otherwise judges some algorithms as not as 'real' than others.

Now that we've explicitly named the methods we can see that the names have a repeating suffix. When methods have a repeating prefix or suffix it's a sign that you have untapped objects hidden within your code. Going through the exercise of giving the branches of the case statement explicit names helps identify these missing objects. Instead of forcing House to know both 1) the values of order upon which it should switch and 2) what to do in every case, we can disperse the 'what to do' logic into other objects.

Example 4 creates a new class for each kind of order.

###Example 4

class House
# ...
def initialize_pieces(order)
case order
when :random
Random.new.order(DATA)
when :mostly_random
MostlyRandom.new.order(DATA)
else
Default.new.order(DATA)
end
end
end
class Default
def order(data)
data
end
end
class Random
def order(data)
data.shuffle
end
end
class MostlyRandom
def order(data)
data[0...-1].shuffle << data.last
end
end

Example 4 creates three new classes, each of which plays the 'orderer' role. Each 'orderer' implements #order to take a list and return it in the correct order.

These classes will be a delight to test. :-)

Example 4a slightly rearranges the case statement (and likely offends some Rubyists, but that's for another day) to make its purpose more obvious.

If the syntax above is new to you remember that the case...end statement returns an object to which you can send a message. This case statement returns a class; line 11 sends new.order(DATA) to that class. Thus, the case statement's responsibility is to return a class that plays the role of 'orderer'; actual ordering is a separate task that happens afterwards.

Example 4a reveals a curious thing. House is initialized on the order symbol, which it immediately converts into a different object. You can think of House as being injected with a behaviorally impaired kind of 'orderer' (the symbol) which it is then forced to convert into a more robust kind of 'orderer' (an instance of Random, MostlyRandom or Default). In the above implementation House depends on (knows about) many things. It knows the names of all possible symbols, the names all of the 'orderer' classes and the mapping between the two. Many distant changes might force changes to House; it would be more flexible if it knew less.

We could spare House many of these dependencies if we inject the object it actually wants, and Example 5 does exactly that. Here, Controller has assumed responsibility for creating 'orderer's and injecting them into House (line 13).

The responsibility for converting feeble 'orderer' objects into more robust ones belongs no more in Controller than it did in House, but this new code is an improvement. It's best to do these kinds of conversions at the first opportunity and at least now we're pushing the conversion back up the stack, searching for its natural home.

With this change House becomes open/closed to new 'orderers'; you can inject any object you like as long as it implements #order. House also has fewer dependencies; it can collaborate with new 'orderers' without being forced to change.

The Controller#orderer_for method, however, is not yet open/closed; it must change if you add new 'orderers'. If you're willing to commit to a naming convention and do a bit of metaprogramming (as in Example 6), this is easily remedied.

As long as you follow the naming convention this code will convert any symbol to an instance of the corresponding class.

Controller's #orderer_for method was uncomfortable when it was merely in the wrong place but now that we've complicated the code in the name of making it open/closed it feels increasingly important to figure out where the method belongs. We have a number of things that revolve around the concept of 'order' (three classes and this factory method) and this code would be easier to understand if they all lived together. Example 7 creates an Order module to hold them.

Moving the factory method to the Order module makes it natural to change its name from #orderer_for (as in Example 6 line 3) to #new (above, line 2). The #new method of Order takes a symbol for an argument and returns the right 'orderer'. You need not care about the class of the returned object; the thing you get back responds to #order and that's good enough.

This refactoring is complete, and I have just one final thought before we return to the original problem of 'code shapes'.

I totally understand that this is a small example and that these techniques can feel like overkill for a problem of this size. Perhaps they are; I wouldn't resist if you insisted it were so. However, there are bigger problems for which these techniques are the perfect solution and I rely on your ability to see the larger abstraction. You can't choose whether to use these techniques unless you know them and it's much easier practice on a small example like this.

###Example 2: Reprise
And now, back to the idea that some code shapes are better than others. Here's a reminder of Example 2, the code that was written to meet the first new requirement.

At first glance this code seems fine but its shape hides objects that we found during the refactoring. Line 4 hides Order::Random and line 21, Order::Default.

We can easily expose these objects by rewriting the code in a more explicit, straightforward way. The code below replaces the #pieces method with an else branch in the if statement and adds an attr_reader for @pieces.

Once the if statement on line 6 is written this way we can see that it uses the value of the boolean random to choose the algorithm to apply to DATA. This is a form of
primitive obsession.
The booleans true and false should be replaced by more robust 'orderer' objects that provide these algorithms and which are injected into House in their stead.

The arrangement of the code in the original Example 2 hides these objects, the code above reveals them.

###Summary

Code shape matters, especially when it comes to conditionals. Dividing a conditional into multiple parts and placing those parts far apart makes it hard to see underlying objects. The opposite is also true; clarity can be achieved by hunting down all the parts of a conditional and putting them back together.

When conditionals are shaped correctly it's easy to see and extract missing objects. Once extracted, these more robust objects can be re-injected in place of the original primitives. When House was injected with an 'orderer' it became both more consistent and more flexible. The likelihood that it will be forced to change went down and its ability to collaborate with objects it knows little about went up.

And finally, the 'default' is often just another kind of specialization. Negative space is as valid as positive; in the
Rubin Vase image
the vase and the face are equally real. Recognizing that the default case is in the same category as all the other specializations allows you to inject an object that does the right thing, and objects that can be trusted to do the right thing make everything easier.

This exercise was extracted from my Practical Object-Oriented Design course, which is chock full of stuff like this.

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

]]>Getting It Right By Betting On Wrong Sandi MetzWed, 28 May 2014 17:15:00 +0000https://www.sandimetz.com/blog/2014/05/28/betting-on-wrong537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:53bc84fce4b0c25bf8d28dd3Imagine driving from Denver, Colorado, to Death Valley National Park.
You get on I-70 and head west, climb the front range, cross the high peaks
of the Rockies, descend Colorado’s western slope into Grand Junction and
then make your way across the Utah dessert to Fishlake National Forest,
which, if it’s springtime, is likely on fire. Here you collide with I-15
and turn left to head south.
In the deepest dark of night, eleven hours into your drive and two short of
your destination, the long smear of the Milky Way Galaxy fades behind the
neon lights of Las Vegas. Against your better judgement you stop and wander
into a casino. They immediately recognize you as a programmer and force you
to choose one of two sides in a permanent bet.
The bet is whether the code you wrote in the last month is ‘right’ or
‘wrong’. The casino defines ‘right’ as ‘Will not change for any reason
within the next 6 months’. You’ll win if you bet on ‘right’ and your code
doesn’t change, or if you bet on ‘wrong’ and it does.Imagine driving from Denver, Colorado, to Death Valley National Park.

You get on I-70 and head west, climb the front range, cross the high peaks of the Rockies, descend Colorado’s western slope into Grand Junction and then make your way across the Utah dessert to Fishlake National Forest, which, if it’s springtime, is likely on fire. Here you collide with I-15 and turn left to head south.

In the deepest dark of night, eleven hours into your drive and two short of your destination, the long smear of the Milky Way Galaxy fades behind the neon lights of Las Vegas. Against your better judgement you stop and wander into a casino. They immediately recognize you as a programmer and force you to choose one of two sides in a permanent bet.

The bet is whether the code you wrote in the last month is ‘right’ or ‘wrong’. The casino defines ‘right’ as ‘Will not change for any reason within the next 6 months’. You’ll win if you bet on ‘right’ and your code doesn’t change, or if you bet on ‘wrong’ and it does.

The tricky part is this—you only get to choose once. You have to decide, right now, before you leave the casino, whether to bet all of your future income on each month’s code being ‘right’ or ‘wrong’. Once you chose, when you win the bet in a future month the casino will double your money, and when you lose, they’ll take your paycheck.

So, how do you bet, ‘right or ‘wrong’?

While you ponder this, let’s have a look at some code.

Example 1: Straightforward implementation

class House
def recite
(1..pieces.length).map {|i| line(i)}.join("\n")
end
def line(number)
"This is %s.\n" % pieces.last(number).join(' ')
end
private
def pieces
[
'the horse and the hound and the horn that belonged to',
'the farmer sowing his corn that kept',
'the rooster that crowed in the morn that woke',
'the priest all shaven and shorn that married',
'the man all tattered and torn that kissed',
'the maiden all forlorn that milked',
'the cow with the crumpled horn that tossed',
'the dog that worried',
'the cat that killed',
'the rat that ate',
'the malt that lay in',
'the house that Jack built',
]
end
end

This code produces the nursery rhyme ‘The House that Jack Built’. Let’s allow this simple example to stand proxy for a real world application.

What kinds of things might change?

The recite method might change. For example, the reciters might decide that they sometimes want two newlines after each line.

The hardcoded ‘This is’ string that starts each line might change. Perhaps some reciters feel more confident than others and wish to start lines with ‘This definitely is’.

The strings in pieces might change. Maybe the dog annoys the cat in addition to worrying it.

Any of these things might happen and you could add code, right now, to handle each possibility.

Perhaps you believe that some reciters will be more certain than others. Example 2 changes the code to support both the current and the anticipated requirement. It accepts a parameter (line 4) that’s used in an if statement (line 15) to determine how each line should start.

Now House.new.recite displays the original rhyme and House.new(true).recite the new one.

Example 3 shows an alternative that accomplishes the same thing another way. It avoids the if statement that was added in Example 2 by injecting the words that start each line, using a default (line 4) in order to continue to meet the current requirement.

Example 3: Injecting line_start

Example 4 contains the complete listing using the code from Example 3.

Example 4: Injecting line_start (complete listing)

class House
attr_reader :line_start
def initialize(line_start="This is")
@line_start = line_start
end
def recite
(1..pieces.length).map {|i| line(i)}.join("\n")
end
def line(number)
"#{line_start} %s.\n" % pieces.last(number).join(' ')
end
private
def pieces
[
'the horse and the hound and the horn that belonged to',
'the farmer sowing his corn that kept',
'the rooster that crowed in the morn that woke',
'the priest all shaven and shorn that married',
'the man all tattered and torn that kissed',
'the maiden all forlorn that milked',
'the cow with the crumpled horn that tossed',
'the dog that worried',
'the cat that killed',
'the rat that ate',
'the malt that lay in',
'the house that Jack built',
]
end
end

Here House.new.recite again displays the original rhyme and House.new("This definitely is").recite the new one.

Notice that this code is a little more abstract than the original. This increase in abstraction makes it easier to change (as long as the change is the one you anticipated!) but slightly harder to understand.

Code is read many more times than it is written. Abstractions add changeability but increase cognitive load. The ones that are actually needed save money, those that aren’t increase costs every time anyone looks at the code.

Anticipatory complexity rarely pays off. Unless your Magic 8-Ball is far better than mine you should avoid the guessing business. Guessing right half of the time means guessing wrong the other half, and the code for wrong guesses confuses everyone who follows. The barrier to introducing a speculative abstraction is very high.

This is where the Open/Closed Principle (OCP) comes in handy. OCP is one of the core object-oriented design principles. It provides both the ‘O’ in ‘SOLID’ and guidance about when to create an abstraction.

Open/Closed is short for the phrase ‘Open for extension but closed for modification’, and this phrase, in turn, means that you should arrange things so that you can add new behavior to an application without changing its existing code.

If you find this idea incomprehensible, you’re not alone; this sounds quite impossible. However, suspend disbelief for a moment and start by imagining a world in which your applications are open/closed, where you can add new behavior without changing the code you have.

In this open/closed world two very powerful things are true:

It’s difficult to accidentally break existing code, and

the tests you have always run green.

This is programming nirvana. Open/Closed is clearly good; the problem isn’t that it’s wrong, it’s that it’s not obvious how to achieve it. We want our code to be open/closed to the next requirement but we cannot know what that requirement will be.

Are we doomed to guess? No.

Open/Closed requires that you write code that is open to the next change but it says nothing about when to do so. It doesn’t require that you guess the future, instead it tells you to write the simplest conceivable code today and then to remove your hands from the keyboard and wait. Future change requests will tell you exactly how you should have arranged the code you have.

When new requests arrive, you’ll rearrange existing code so that you can extend it with new behavior. It’s a two step process, first you refactor existing code to a more felicitous arrangement and then you write new code to implement the change. Kent Beck says it best (but I’ll paraphrase anyway): “Make the change easy … then make the easy change.”

In the absence of an explicit requirement to the contrary, Example 1 is a pleasingly simple way to arrange this code. Once you’re asked to sometimes start sentences with “This is” and other times with “This definitely is”, you’re forced to make a change.

Example 2 is not open/closed to this change. Line 15 interleaves the new code with the old, adding “This definitely is” into the existing mix, which stands a chance of breaking existing code and tests.

Example 3 / 4, in contrast, is open/closed. All of the code is needed by the existing requirement (no new behavior has been added) and rearranging this code to be open/closed to the new requirement was accomplished without changing the existing test. It can now be extended to meet the new requirement by simply passing in a new string. It’s open for extension and closed to modification.

The morals of this story are:

First, you don’t need to guess the future—sit back and wait; it will eventually arrive. Change requests are inevitably for something you did not anticipate.Guesses serve only to muddy the cognition waters during the course of their insufficient lives.

Next, when a new request does arrive, make the change in two steps. Refactor existing code to be open to the new requirement and then “make the easy change”.

Finally, write simple code. It will likely be no more ‘right’ than your guesses but certainly will be easier to change when you figure out what you should have done.

This brings us full circle and solves your quandary in Vegas. Requirements are fleeting. Even code that is absolutely necessary today is likely to change in the future, and the odds of your guesses surviving the test of time are even longer.

The smart money bets on wrong.

Updated News:

Public POOD course in May, 2018 in delightful North Carolina

My next public
Practical Object-Oriented Design course
will be held in Durham, NC on May 2-4, 2018. Yup, it's time for another
POODNC
. This is your chance to spend three days with like-minded peers. Join us, and change how you think about objects.

]]>How Shall We Define Design?Sandi MetzThu, 05 Jul 2012 17:46:00 +0000https://www.sandimetz.com/blog/2012/07/05/how-shall-we-define-design537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:53d29507e4b052a8d0309d71I love object-oriented design. It’s like open source software in general;
perfect strangers come up with ideas that can save me time and money and
then, out of the goodness of their hearts, go to a fair amount of trouble
to pass these ideas on. I benefit from their efforts every day.
However, you don’t have to go far to find competent, thoughtful,
well-intentioned folks who curse and spit when they say ‘design’. To these
folks OOD is anathema; they see it as useless excess that must be avoided
at all costs.
Two things are odd about this situation. First, while I’m certainly no
genius, neither am I a complete idiot. As a woman of a certain age I’ve
been writing code for an alarmingly long time. I have tons of experience
getting applications out the door and my experience is that OOD lowers
costs, increases output and keeps code fun.
Second, when I listen to the anti-design folks explain their
points-of-view, I agree with about 99.9% of what they have to say.I love object-oriented design. It’s like open source software in general; perfect strangers come up with ideas that can save me time and money and then, out of the goodness of their hearts, go to a fair amount of trouble to pass these ideas on. I benefit from their efforts every day.

However, you don’t have to go far to find competent, thoughtful, well-intentioned folks who curse and spit when they say ‘design’. To these folks OOD is anathema; they see it as useless excess that must be avoided at all costs.

Two things are odd about this situation. First, while I’m certainly no genius, neither am I a complete idiot. As a woman of a certain age I’ve been writing code for an alarmingly long time. I have tons of experience getting applications out the door and my experience is that OOD lowers costs, increases output and keeps code fun.

Second, when I listen to the anti-design folks explain their points-of-view, I agree with about 99.9% of what they have to say.

How can I simultaneously love design and agree with almost 100% of the statements made by folks who profess to hate it? Well now, that has become obvious, hasn’t it … we must be using different definitions for the word.

I define the design of an application as the current arrangement of its code, and the act of design as using ones knowledge of the consequences of various code arrangement techniques to solve the current problem in a way that accommodates the next (as yet undefined) change. The anti-design folks (if I may speak for them) use design to mean making guesses about unknown requirements and writing speculative code in an attempt to preemptively meet them. By my definition there’s no such thing as over-design, by theirs the terms design and over-design are synonyms.

My experience is that participants in pro/anti OOD arguments agree far more than they disagree, and when they can’t agree that they do agree it’s because they’re stuck to their own definitions of the word. These disputes are not about core beliefs, they merely reflect a communication gap.

I was thinking about this issue while listening to the Ruby Rogues episode with David Heinemeier Hansson, and serendipitously, in that same episode Chuck mentioned that he’d been experimenting with mind maps using MindMeister.

I sat down to organize my thoughts and ended up with this mind map (drag left to see the whole map, open/close branches with the +/– buttons):

How cool is that? I (for one :–) ) now feel a pleasing sense of clarity. If your thoughts about OOD map a different way, let’s hear about it … or better yet, make a map of your own and share it.

In the meantime, if you’ve written applications that have become painful to maintain, my definition of design offers a way out. The purpose of OOD is to reduce the cost of change and the principles of OOD let you create applications that simultaneously do the simplest possible thing today and stay out of your way tomorrow. The folks who don’t feel the need for design are clearly not having trouble with their apps, but if you’re have trouble with yours, OOD can help.

]]>Ruby Case Statements and `kind_of?`Sandi MetzFri, 12 Jun 2009 18:12:00 +0000https://www.sandimetz.com/blog/2009/06/12/ruby-case-statements-and-kind-of537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:53d29c7be4b0b38230c74105You’re an object – Stand up straight and act like one!
Imagine you have this code:You’re an object – Stand up straight and act like one!

You’re probably familiar with this pattern. Its everywhere in Rails and you likely use it in your own code.

I want to say, in the nicest possible way, that this style of code is wrong, wrong, wrong and that you should do a different thing.

Okay, now that I have your attention, I’m not trying to start a fight. I’m not the best Ruby person around and I’m definitely not the best OO designer, but I do have an alternative pattern to suggest.

I’m aiming to start a discussion, not a religious war. Strong opinions are welcome.

What’s happening up there?

MyView needs to operate on several other objects. It knows:

the classes of all the objects that it can interact with, and

the behavior that should occur for each of those classes.

The case statement above is really an if statement that checks ‘kind_of?’ on each collaborating object.

I object to this code because:

use of kind_of? is a code smell that says your code is procedural, not object oriented, and

if you write procedural code your application will gradually become impossible to change and everyone will hate you.

Why is it wrong?

If I change how double works on any of these classes, MyView must change, but that’s not the real problem. What happens if MyView wants to double some new kind of object? I have to go into MyView and add a new branch to the case statement. How annoying is that?

But that’s the least of it. If I’m writing code that follows this pattern, I likely have many classes that do stuff based on the classes of their collaborators. My entire application behaves this way. Every time I add a new collaborating object I have to go change code everywhere. Each subsequent change makes things worse. My application is a teetering house of cards and eventually it will come tumbling down.

Also, what if someone else wants to use MyView with their new SuperDuper object? They can’t reuse MyView without changing it since MyView has a very limited notion of what kinds of objects can be doubled.

In this example, objects are what they are and because of that they behave the way they do.

That statement is deceptively simple but incredibly important. Objects are what they are so they do what they do.

It is not the job on any object to tell any other object how to behave. Objects create behavior by passing messages back and forth. They tell each other what, not how.

What is the event/notification/request and it is the responsibility of the sender.

How is the behavior/implementation and it should be completely hidden in the receiver.

Code 2 is object oriented because it relies on a network of interacting objects to produce behavior. Each object knows its own implementation and it exhibits that behavior when it receives a message.

No object in your system should have to know the class of any other object in order to know how to behave. Everything is a Duck. Tell the Duck what and the Duck should know how.

This way you can change how any object is doubled by changing its own specific implementation. More importantly, MyView can now collaborate with any kind of object, as long as the object implements double.
This is a nice theory, but it seems impossible in practice.

Nah, it’s easy. But it means thinking about objects in a more Object Oriented way.

In order to write code like Code 2, you have to believe in objects.

In Ruby, everything is an object. I know that you know this, but I suspect that you don’t feel it in your bones. You came from those languages where you couldn’t change String so now you operate as if String (and Symbol and Array and …) are static types.

Throw off your shackles. Ruby is a network of interacting objects and you can add behavior to all of them. When you find yourself saying, if you’re this kind of thing, do this, but if you’re that kind of thing, do that, it’s your cue to push the behavior back into those individual objects.
Sound bites.

Always send messages if you can.
Implement behavior only when there’s no one left to ask.
The last object that could possibly receive the message is the object that should implement the behavior.
Therefore:

]]>SOLID Design Principles - Dependency InjectionSandi MetzSat, 21 Mar 2009 10:33:00 +0000https://www.sandimetz.com/blog/2009/03/21/solid-design-principles537c0374e4b0f52ed92942e6:53817322e4b0ac7f7a7b9c2f:53d383e7e4b00410ab99bf8aNothing is more pleasing than beautiful code. And nothing is more
heart-breaking than watching beautiful code get destroyed.
Lately, I’ve been paying particular attention to SOLID Object Oriented
Design (OOD) principles and their interaction with TDD. I’m finding that,
while TDD is an essential first step, it just isn’t enough. If I want my
code to survive the rigors of change and be useful for a long time I need
to armor it by following SOLID principles.
There’s a delightful symmetry in the feedback loop between TDD and OOD.
TDD in isolation is not guaranteed to produce well designed code, but if
you follow the OOD principles while writing the code you’re testing, TDD
gets easier and your tests get better.
In case you feel like pushing back already, let me add an early caveat. If
you...Not as Painful as it sounds…

Nothing is more pleasing than beautiful code. And nothing is more heart-breaking than watching beautiful code get destroyed.

Lately, I’ve been paying particular attention to SOLID Object Oriented Design (OOD) principles and their interaction with TDD. I’m finding that, while TDD is an essential first step, it just isn’t enough. If I want my code to survive the rigors of change and be useful for a long time I need to armor it by following SOLID principles.

There’s a delightful symmetry in the feedback loop between TDD and OOD. TDD in isolation is not guaranteed to produce well designed code, but if you follow the OOD principles while writing the code you’re testing, TDD gets easier and your tests get better.

In case you feel like pushing back already, let me add an early caveat. If you

But if you’re living in my reality, have a listen to Uncle Bob. In Design Principles and Design Patterns he describes good software as
"clean, elegant, and compelling", with "a simple beauty that makes the designers and implementers itch to see it working".

But then goes on to say:

"What goes wrong with software?
The software starts to rot. At first it isn’t so bad. An ugly wart here, a clumsy hack there, but the beauty of the design still shows through. Yet, over time as the rotting continues, the ugly festering sores and boils accumulate until they dominate the design of the application. The program becomes a festering mass of code that the developers find increasingly hard to maintain."

There’s more, but if I told you all of it I’d have to send you to the dermatologist.

If you have good tests, you can protect the reliability of any smelly pile of software, even if it makes you cry when you have to change the code. $$’s fly out the window but it all still works.

If you have good tests AND good design, the software will be reliable and changes will be an economical pleasure. You’ll look forward to adding new features so you can undertake big refactorings and make the code do even more.

But, enough with all this talk. Let’s do something.

Dependency Injection

Bob calls it Dependency Inversion and you could definitely argue that the two concepts are slightly different, but don’t quibble with me about this. I’m being practical here.

What does this spec test? Job? Or Job AND FileRetriever AND FileCleaner? Obviously, all three. My spec is testing a whole set of objects; Job and all of it’s dependencies. It’s fragile in that it relies on objects other than the one under test and it runs too long because it exercises code that it should not care about.

Mocks/stubs to the rescue, right? I could stub FileRetriever.get_file and FileCleaner.clean and bypass both of those problems. However, even if I stub those methods, my code still has a bad smell. Stubbing improves the test but does not fix the flaw in the code.

Because of the style of coding in Job, it contains dependencies that effect my ability to refactor and reuse it in the future. Let’s move some code around.

Now I’m injecting the dependencies into Job. Suddenly, Job feels a lot less specific and a lot more re-usable. In my spec I can create true mock objects and inject them; I don’t have to stub methods into existing classes.

That stylistic change helped a lot, but what if I want to provide some, but not all, of the arguments? It’s easy, just change the initialize method. It wouldn’t bother me if you also wanted to simplify run.

That feels really different from example 1. A simple change in coding style made Job more extensible, more reusable and much easier to test. You can write code in this style for no extra cost, so why not? It will save someone pain later.

Example 4 – Pain:

Here’s some code from Rails that generates xml for ActiveRecord objects. (Please, I’m not picking on them, this just happens to be a good example that I dealt with recently.)

Without recounting the whole story, I wanted to use to_xml with a different Serializer class. Imagine how easy it would be if XmlSerializer had been injected into to_xml. Instead, look at it and despair. I have to override the entire method just to name a different serializer class, with all the future maintenance burden that the change entails.

The to_xml code does exactly what it’s supposed to do and in that way cannot be faulted. The person who wrote it isn’t bad, they just never imagined that I would want to reuse it this way.

Let me repeat that.

They never imagined how I would reuse their code.

The moral of this story? The same thing is true for every bit of code you write. The future is uncertain and the only way to plan for it is to acknowledge that uncertainty. You do not know what will happen; hedge your bets, inject your dependencies.

TDD makes the world go ‘round. It lets us make unanticipated changes with confidence that our code will still work, but SOLID design principles keep the code ’clean, elegant, and compelling‘ after many generations of change.

Notes:

I don’t mean to be overly familiar; it’s not like I know the man. But he’s an icon, how can I avoid calling him ‘Bob’?