Cendyne.dev Posts

Approaching projects - Part 5 - 2021-11-10

Do 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.

The ex-CTO / founder of my current employer YOLO'd a lot. He would experiment and introduce new toys over the weekend, deploy on fridays at five PM, and then be bogged down with meetings all week so he never cleaned up after himself. I actually told him that I did not have confidence in his contributions. He actually listened to me and changed his behavior.
ughhh
table-flip
We have so much technical debt because of this that it regularly stalls our new engineers years later. Please, don't reinvent a database ORM. Please, don't reinvent GraphQL based on one week of experience. Please, don't reinvent an MVC tomcat servlet framework. Please, don't reinvent async work queues.

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?

Way back when with shared university mainframes and all that, I wonder how programmers thought about their program before the arduous work of getting it loaded on the mainframe with hole punch sheets. Only to get the answers hours later... and it might have been run against someone elses data punch cards.
laptop
heart
One of my university professors met their partner, a clerk for the university computer, when submitting hole punch program sheets for their grad studies.

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.

Draw the rest of the owl

Like a mathematical proof, every step must be justified or given. And like making proofs, plans aren't fun for most people.

An example mathematical proof

confused
I was never all that gifted in math. But when it came to logical stuff I was able to work with that. My way was to visualize a network of conditional relationships and see if they connected right a whole. But once it was too big to fit in my head, I struggled with it. Thankfully, most solutions can be thought of with some abstraction.

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?

Even mathematical notation is a language invented to permit novel and useful ways of manipulating ideas in the domain of numbers and more. Or so I'm told on the internet, back in the day mathematicians used poems and spatial models to think about solving complex problems. Yet when it came to negative numbers they could not conceive of it. The language of 2d squares and 3d blocks could not support the ideas.
math

Another approach

So if words like this aren't working out for you...

An example task description

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.

Joining the table with wood glue

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.

I hear that venture capitalists and investors care more about a business reliably hitting projections and promises as opposed to exceeding expectations. If a business does not know why they are succeeding, then they probably don't know when they will start failing.
gendo

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:

Then it is time to create tickets. This comes in the next post!

The main takeaways of this post are:


This post is the fifth of the series, part 6 on documenting a plan with tasks comes next.

excited
You can succeed! If you're finding it hard to assemble your thoughts in one written medium, try another. Determining your resource consumption is a hard problem. That includes how much time you take! But if your bread and butter is solving problems in Java, C#, PHP, or Haskell then you can reorient project planning to your language of choice.
Ever wondered how math proofs prove something is NP-Hard? They translate their problems into another known NP-Hard problem. Academics can be confident in declaring properties of new models by reorienting their chosen model to another well known and studied model. You can see plenty of this on StackExchange.
math