Approaching projects - Part 5
- 12 min read - Text OnlyDo you have a project, a skill, a goal you'd like to complete or progress? Are you on your own, lacking leadership and structure, or could use more of it for yourself and your team? Here's what I've discovered works for me as a solo software developer and later as an engineering manager. This is a series of posts, the next post part 6 on documenting a plan with tasks comes next.
In the last post Approaching projects - Part 4, I described how to prepare for an actionable plan by vetting prototypes and finalizing resource needs. This post will focus on creating an actionable plan.
Throughout this series of posts I will be using this user story: As a person with money, I want a custom computer desk to fit in a weird spot in my home. With the chosen solution: Build the desk myself.
Creating a plan
A plan is a procedure to get something done for the first time. It comes with risk, it comes with unknowns. The riskiest plans come without any steps. "YOLO" "lets wing it" may be energizing to some, but they do not inspire success. In fact, deliverables made without plans are pretty low quality.
So let's talk about what makes a plan, what makes a procedure. A logical sequence of steps! Some steps can be performed concurrently. Some cannot, either due to depending on a prior step or contending for a shared resource. Really, tell me if you've watched TV, bathed, and done your taxes all at once.
In a way, plans are also like programs. They come wind conditions, branches, loops, and so on. But you're writing it for the first time and executing it. Oh! But wait! When you're programming, you run it and review the results, revising until it meets expectations right? Well, project plans don't have the time and resources to redo it five times like unicorn venture capital funded companies like Uber. So take the time to measure and simulate each step. Consider if each step's preconditions are sane. That the postconditions are accounted for. Even when there's an exception. How will you handle exceptions?
Like a program, a plan should not skip steps. Sure, the people involved may be able to infer steps that are skipped and do them anyway. But enough of those will add up to confusion, producing the wrong thing, or excessive or a surprising use of resources.
Like a mathematical proof, every step must be justified or given. And like making proofs, plans aren't fun for most people.
Yet, if you think of it like a program instead of a mathematical proof, how does this change your approach? How does your perspective shift if you switch it to a language you're familiar with?
Another approach
So if words like this aren't working out for you...
Then try something else before writing tasks.
interface SawHorse {}
interface WoodPlank {}
interface WoodSurface {}
interface Sander {
void sand(WoodPlank plank);
void sand(WoodSurface wood);
}
interface Jointer {
void edge(WoodPlank plank);
}
interface RouterBit {}
interface Router {
void loadBit(RouterBit bit);
void route(WoodSurface surface);
}
interface Clamp {
void clamp(Object... objects);
void removeClamp();
}
interface WoodGlue {
WoodSurface join(WoodSurface surface, WoodPlank plank);
WoodSurface join(WoodPlank plank1, WoodPlank plank2);
}
interface Me {
void put(Object above, Object below);
void remove(Object object);
void waitFor(long amount, TimeUnit unit);
void flip(Object object);
}
class Plan {
WoodSurface assembleSurface(
Me me,
Queue<WoodPlank> planks,
Queue<Clamp> clamps,
WoodGlue glue,
Jointer jointer,
SawHorse sawHorse1,
SawHorse sawHorse2,
Sander sander,
Router router,
RouterBit bevelBit,
WoodSurface flatReferenceSurface) {
// Prepare each plank so it can be used
Queue<WoodPlank> edgedPlanks = new ArrayDeque<>();
for (WoodPlank plank : planks) {
// Sand each plank so it is clean and smooth
sander.sand(plank);
me.flip(plank);
sander.sand(plank);
// Make the edges straight with the jointer
jointer.edge(plank);
me.flip(plank);
jointer.edge(plank);
// Add to a new pile
edgedPlanks.add(plank);
}
// Set up a reference surface
// This way when the glue hardens, it has the same flatness as the reference
me.put(flatReferenceSurface, sawHorse1);
me.put(flatReferenceSurface, sawHorse2);
// Prepare the surface, glue each plank to the next
WoodPlank firstPlank = edgedPlanks.remove();
Clamp firstClamp = clamps.remove();
// We need it to hold still
firstClamp.clamp(firstPlank, flatReferenceSurface);
// Create the first surface, consisting of only two planks
WoodPlank secondPlank = edgedPlanks.remove();
WoodSurface surface = glue.join(firstPlank, secondPlank);
// For the remaining planks, continue to glue them together
// The last plank will be used too
WoodPlank lastPlank = secondPlank;
for (WoodPlank plank : edgedPlanks) {
// Make the surface larger by adding to it.
// In a way, this creates a new surface.
surface = glue.join(surface, plank);
// We will use the last plank later.
lastPlank = plank;
}
// Now we clamp along the ends
Clamp bigClamp1 = clamps.remove();
bigClamp1.clamp(firstPlank, lastPlank);
Clamp bigClamp2 = clamps.remove();
bigClamp2.clamp(firstPlank, lastPlank);
// And the center
Clamp bigClamp3 = clamps.remove();
bigClamp3.clamp(firstPlank, lastPlank);
// Then we add clamps on the remaining 3 corners to the reference surface.
Clamp secondClamp = clamps.remove();
secondClamp.clamp(firstPlank, flatReferenceSurface);
Clamp thirdClamp = clamps.remove();
thirdClamp.clamp(secondPlank, flatReferenceSurface);
Clamp forthClamp = clamps.remove();
forthClamp.clamp(secondPlank, flatReferenceSurface);
// It takes time for the glue to cure
me.waitFor(4, TimeUnit.HOURS);
// Remove all the clamps, you may finally see here how many we had to use.
firstClamp.removeClamp(); // 1 clamp
clamps.add(firstClamp);
secondClamp.removeClamp(); // 2 clamps
clamps.add(secondClamp);
thirdClamp.removeClamp(); // 3 clamps
clamps.add(thirdClamp);
forthClamp.removeClamp(); // 4 clamps
clamps.add(forthClamp);
bigClamp1.removeClamp(); // 5 clamps
clamps.add(bigClamp1);
bigClamp2.removeClamp(); // 6 clamps
clamps.add(bigClamp2);
bigClamp3.removeClamp(); // 7 clamps
clamps.add(bigClamp3);
// But we're not done yet. Now we need to clean the surface.
// Wood glue will seep out and it needs to be sanded off.
// We no longer need the reference surface
me.remove(flatReferenceSurface);
me.put(surface, sawHorse1);
me.put(surface, sawHorse2);
// Remove the dried wood glue that seeped between the boards
sander.sand(surface);
me.flip(surface);
sander.sand(surface);
// Route each side of the top of the table
router.loadBit(bevelBit);
router.route(surface); // Left
router.route(surface); // Right
router.route(surface); // Back
router.route(surface); // Front
return surface;
}
}
This exercise above highlights a few interesting resource needs.
- I see that large clamps are required which can extend as long as the surface is deep
- I need several more clamps to keep things in place.
- I need a reference surface (actually more in reality).
Reality differed a little. For the reference surfaces, I used several boards clamped together. Some of these boards came from other projects (A book shelf) I had planned for a later time. Instead of saw horses, I actually used the finished frame of the table. Underneath, I used wax paper to catch the drippings from the wood glue.
Anyway, this exercise of using a programming language allows us to do a bit more analysis and try a few more what-ifs. For example, what if we had a second person? Could sanding and jointing be done concurrently? Not on the same planks, but perhaps with a buffer between. Another spot might be that two people could glue up two halves of the table surface and join them at the end.
More questions might be, what if we have two people, and two sanders? These resources can be negotiated upfront before the project begins. You'll look better executing plans on time and on budget, and it'll look even better if you set the budget and set the time.
Of course, if your plan reveals that you're using too many resources and you cannot request as much budget or time or whatever that you need, then you need to make some compromises, adjustments, and so on.
This program is a prototype that you can alter, that you can examine, and even peer review. It may be novel, it may be different, but a program language may be just the tool your peers need to understand what resources you need, what depends on what, and give feedback on the plan's soundness.
When you are confident that:
- You understand the User Story
- A proposed solution satisfies the story
- The proposed solution is possible
- Milestones for the solution are defined
- You have models and references for steps that support each deliverable
- A reasonable sequence and dependency graph of steps to create each deliverable is drafted
Then it is time to create tickets. This comes in the next post!
The main takeaways of this post are:
- Plans are procedures, which are composed of logical steps that consume resources
- Programming languages map well to describing plans
- With a draft plan written, examine the resources and begin negotiating for resources
- Review the draft plan with peers and consider the soundness of its assumptions and design
- Revise the plan with the resources available and resources promised
This post is the fifth of the series, part 6 on documenting a plan with tasks comes next.