The Willfully Ignorant Development Process

Comments

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.

comments powered by Disqus