Links

The Willfully Ignorant Development Process

Almost a year and a half ago, I wrote a post entitled
"The Greenfield App Continuum". It
didn't get a ton of attention at the time. Then again, maybe it didn't really
deserve much attention. It was heavy on theory, and light on application.
That's because, at the time, it was only theory to me. That theory being: If
we start building an application as though we are completely ignorant of the
constraints our persistence layer will eventually impose on us, we'll end up
with an appication that's more pleasant to develop as a result.

I can't pretend that I'm the only person who's ever had that thought. Heck, even
a comment on that
article points to a book entitled
"Applying Domain-Driven Design and Patterns", and in Domain-Driven Design,
"Persistence Ignorance" is a thing. With a name and everything. However, it may
be telling that the rest of the book title is: "With Examples in C# and .NET".
So, when I was writing that post in 2012, even as I knew what I was writing
wasn't an original thought, as a Ruby (and Rails) developer, it certainly
felt novel.

And, again, at that point, it was just theory, to me. I hadn't tried it. Since
then, I have. And I've been meaning to write about that for a while now.

I'm calling it "The Willfully Ignorant Development Process."

Setting Your Inner Idealist Free

When we defer making any persistence choices up front, we get to spend more time
thinking about the interesting (read: difficult) problems specific to our
application.1 We call this
"domain modeling," and though there are people who might make it sound much
more complex than this, domain modeling is really just coming up with words
that describe the problems we're trying to solve, and the ways in which we'll
go about solving them. In an object-oriented language like Ruby, those words
will express themselves as classes, attributes, and messages (methods).

And because Ruby is so low-ceremony and so pleasant to write, there's not much
difference in effort between thinking about our domain model and actually
coding it!

And so it was that I began doing inside-out TDD of an application with complex
requirements, yet spent the first two weeks without ever typing
"< ActiveRecord::Base".

Each object in my new Rails app's app/models directory started its life as a
class of its own, with distinctive characteristics under test that explained
their reason for existing. Forcing each class to justify its existence for
reasons beyond "I need a table to store this data" turned out to be a huge
win. It's not surprising, when we step back and think about it. But when
we're happily generating model after model in a Rails app, and wiring up CRUD
actions in controllers, giving ourselves the illusion of progress, it's easy to
forget that we're ignoring the hard part. And, no matter how practiced I get,
I catch myself falling into this trap unless I set rules to prevent it.

Telling the Story

In That's Not Very Ruby of You,
I made the case that we are storytellers, and that Ruby is the language we use
to tell our stories. It's important that we make every word of that story tell,
and building the domain model first is a great way to find the words that frame
the problem in the way we'd like it to be viewed.

Think about it this way: each decision that we make during the development of
an application is, by necessity, a narrowing of our scope. Defining what our
application is is required to define what it isn't, and vice versa, just as
a sculptor's creation emerges once the parts that don't belong are removed from
a block of stone. The real art in programming is figuring out how to make the
fewest decisions necessary to create a program that does what we need it to do.

So, then, it stands to reason that we'll never have more freedom to influence
the way that people (ourselves included) reason about an application than before
we've chosen the words that represent that way of thinking. Words matter. They
give us convenient hooks on which to hang various concepts, and the words we
choose carry preconceptions about the things they describe.

Side note: This is, I believe, why "naming things" is one of the hard problems
of computer science. Naming things well requires a recognition of what they
are, or will be, and since we generally need an identifier to attach behavior
to in order to implement the behavior in the first place, it would seem to
require some degree of precognition. Our difficulty in naming things is a
manifestation of our difficulty in reasoning about a given problem.

Writing domain models first allows us to experiment with different ways of
thinking about the problem with very little friction. And somehow, for me at
least, knowing there aren't tables in a database with the names I chose removes
a small but perceptible disincentive to change. When we're just writing a few
Ruby classes, there's no need to run a migration or change a bunch of mocks in
order to test an idea.

What's next?

Once we've done enough playing around with different ways of thinking about the
problem, we should have a good idea of how we'd like to think about the
problem. That's a huge step. I'd argue that the biggest value of "design
patterns" isn't the patterns themselves, but that they have names. Likewise for
our code. Once things have names, those "hooks" on which we can hang concepts
exist. You can meaningfully discuss the model with another human being.

In fact, that's become a heuristic I've started to use. If I can't have a
discussion about my code without sitting someone down in front of a monitor and
pointing at specific lines on the screen, then I haven't named enough things.

Now that we've determined our ideal way of thinking about the problem, and we've
even got some nifty running code that shows that this way of thinking about the
problem can yield the desired results, it's time to make concessions to the
real world. This means we might be making adjustments to map our domain model to
a persistence model and, in a Rails app, deciding what concessions we're willing
to make to "The Rails Way."

I had a few false starts in that area, and arrived at a set of basic rules for
the application I built after some experimentation. But that's a topic for the
next post.

[1] Yes, scaling is an interesting
and difficult problem. It is, however, not likely one which you are yet
encountering in your greenfield application, nor one that is unique to your
application. Lots of applications need to scale.