Posted in: Sparks - Ideas, connecting the dots
"Done" is relative to what comes next
By Ryan Singer •
Right now I'm at the very beginning of a project with lots of unknowns. Starting it exposed me to a common pitfall where scope expands very early in the first steps of a project.
I want to prototype a drag and drop interface to do this kick-off exercise. My plan of attack looks like the screenshot below. I sketched eight scopes at the start based on a handful of imagined tasks that I dumped. I wanted to get into "Drag and drop" and "Persist positions" as fast as possible because they are the most valuable with the most unknowns. To get to those, I needed to make a basic layout of boxes and tasks in CSS that renders inside an app template in CSS ("Box layout").
When I started on the "Box layout" scope, I ran into the scope expansion problem.
In theory the scope was about getting an HTML template in place on a Rails app with some CSS layout, so the elements would be ready for experiments with drag and drop interactions.
But setting up the Rails app sparked discovered tasks. I had to set up a controller action accessible via some route to render a template. This screen is in the "middle" of the app. It's not the first thing you see, and it probably belongs to some bigger thing like a Project and/or an Account. The Scopes and Task data that I wanted to render on this template probably should hang off those somehow. As I opened the routes config file, I wondered ... do I need to include the project in the route, like `projects/:id/scopes`? If so, should I generate a basic project model now? Should I nest the scopes_controller in a 'project' module, anticipating the nesting I'll want later? What about the tasks I want to render in the template? Should I make some fixtures for them in the database first so I don't have to hard-code them in the template, which would lead to lots of duplicated markup when I'm iterating on the layout?
These questions easily turn a 15-minute stub into a day-long project full of sidetracks. The thing is, all of those things do need to be done eventually. The logic is seductive: "I'm going to need it later, so shouldn't I do it now?" As if I'm not really "done" and I'm cheating by skipping things like the database schema and properly nested controllers.
Yes I'm going to need those things later. But not immediately later. Later later. Right now, I'm trying to get to a place where I can answer unknowns about drag and drop that make or break my ability to do this project.
Thinking about that next thing — the drag and drop scope — strengthened my resolve to take the shortest path.
Instead of wiring up a bunch of stuff I knew I would need later in the app, I made a dumb route right at the root (`resources :scopes`), generated a corresponding controller, and populated it with data literals:
After that, I had what I needed to start laying out the template without a lot of duplicate markup:
As soon as these are rendered into a grid of boxes that matches the basic concept I have in mind, I'll be ready to move on and start prototyping the drag and drop questions I'm anxious about.
This is an instance of "right to left" thinking. By putting something on the "right" of the thing we're doing (after it, sequentially), a path dependence is introduced. Analyzing that dependence tells us what should be in and what's out of scope on the left in order to make the thing on the right happen next.
Before, there was nothing on the outside telling me when I was done with that preparatory layout scope. It had a name and a supposed purpose, but it was effectively open-ended.
Putting another scope to the right of the one changes everything. Now there's a goal — something to feed into. The scope on the left needs to do whatever the next thing requires, and not more. I can stop when the current scope creates the output required by the next scope as input.
Thinking about this gives me a new appreciation for the role of sequence in scoping. Scoping isn't just about bundling tasks into completable units. The very sequence of the scopes informs what "completed" means in a path-dependent chain from right to left.