Author: milo.todorovich@gmail.com

  • Composing a Cup of Coffee from Components

    Composing a Cup of Coffee from Components

    You can learn a lot about software design by thinking about your first drink of the day.

    Anyone heading in to work isn’t themselves without that first infusion of caffeine. Whether it’s at the drive-thru or the counter, you make your order, then grip, sip, and go.

    What does that have to do software design?

    Follow me down this line of questions, I will show you how composable design can improve your software.

    First, how do you use a cup of coffee? Typically, you grip the cup, then take a sip.

    Hand-drawn image of a cup labeled “Drink”.

    What if it’s a really hot cup of coffee? You’re server may give you a cup with a sleeve. And how do you take a drip of hot coffee? Grip, then sip.

    Hand-drawn image of a cup with a sleev labeled “HOT Drink”.

    What about when you’re on the move? In that case, your server can give you a cup with a lid. And how do you drink your coffee when you’re late and moving fast to the office? Grip, then sip.

    Hand-drawn image of a cup with a lid labeled “MOVING Drink”.

    What if I’m not drinking coffee? Juice, water, soft-drinks, the rules are still the same. Grip, then sip.

    So the rules around using a cup — for coffee, juice, or anything else you drink in the morning — involve two steps: grip and sip.

    In software terms, we would say that the interface for a drink has two methods: grip and sip.

    public interface Drink {
    public void grip();
    public void sip();
    }

    But I need to drink my half-caff, no-foam, one-pump, extra-hot machi-latte while I’m running late!

    Hand-drawn image of a cup with a sleeve and lid labeled “HOT MOVING Drink”.

    Grip, then sip.

    It doesn’t matter whether your language supports interfaces, protocols, or duck-types. What matters is that when someone asks for a drink, they expect to grip and sip.

    That’s easy enough for you. But what about your barista? How can they keep up with all of these different requests.

    How would that look in software?

    Well, a regular cup is simple enough. We could use some popular OO language to implement the coffee this way.

    public class Cup implements Drink {

    private String liquid;

    public Cup(String liquid) {
    System.out.println("Pouring your new " + liquid);
    this.liquid = liquid;
    }

    public void grip() {
    System.out.println("Gripping " + this.liquid);
    }

    public void sip() {
    System.out.println("Sipping " + this.liquid);
    }

    }

    You could buy a bunch of different cups: regular cups, lidded cups , sleeved cups, and sleeved-and-lidded cups. This solves the problem, but now you’ve got so many combinations to think about.

    The manufacturer has to decide if your sleeved, lidded cups is more similar to the sleeved cups, or to the lidded cups.

    public class Cup implements Drink {} // just like before

    public class HotCup extends Cup {

    public HotCup(String liquid) {
    super(liquid);
    }

    public void grip() {
    System.out.println("Protecting your hands.");
    super.grip();
    }
    }

    public class LiddedCup extends Cup {

    public LiddedCup(String liquid) {
    super(liquid);
    }

    public void sip() {
    System.out.println("Protecting your shirt.");
    super.sip();
    }
    }

    // TODO -- which way should it be?
    public class HotLiddedCup extends LiddedCup {}
    public class LiddedHotCup extends HotCup {}

    The barista also has to get it right the first time.

    public enum OrderType {
    HOT,
    FAST;
    }

    public Drink processOrder(String liquid, OrderType... types) {
    List<OrderType> ots = Arrays.asList(types);
    Drink d;
    if (ots.contains(OrderType.HOT) && ots.contains(OrderType.FAST)) {
    d = new HotLiddedCup(liquid);
    } else if (ots.contains(OrderType.HOT)) {
    d = new HotCup(liquid);
    } else if (ots.contains(OrderType.FAST)) {
    d = new LiddedCup(liquid);
    } else {
    d = new Cup(liquid);
    }
    return d;
    }

    If you change your mind, you have to go back to the counter and ask for a different type of cup. Are you sure you want to upset your barista that way? Besides, you’re running late.

    A Different Approach

    Luckily, the coffee store found a company that makes cups, sleeves and lids that all work together.

    Hand-drawn image of a cup, sleeve, and lid.

    The barista reads your mind, puts together the right parts, and hands you your drink. That always work well.

    Again, the manager comes to the rescue by putting out some extra lids and sleeves near the cream and sugar. So you’re set.

    This is the gist of composing your solution from small parts that work together.

    What could that look like in code?

    public class Cup implements Drink {} //same as before

    public class SleevedDrink implements Drink {
    private Drink drink;

    public SleevedDrink(Drink d) {
    this.drink = d;
    }

    public void grip() {
    System.out.println("Protecting your hands.");
    this.drink.grip();
    }

    public void sip() {
    this.drink.sip();
    }
    }

    public class LiddedDrink implements Drink {
    private Drink drink;

    public LiddedDrink(Drink d) {
    this.drink = d;
    }

    public void grip() {
    this.drink.grip();
    }

    public void sip() {
    System.out.println("Protecting your shirt.");
    this.drink.sip();
    }
    }

    // Making a drink becomes much simpler....
    public Drink processOrder(String liquid, OrderType... types) {
    List<OrderType> ots = Arrays.asList(types);
    Drink d = new Cup(liquid);
    if (ots.contains(OrderType.HOT)) {
    d = new SleevedDrink(d);
    }
    if (ots.contains(OrderType.FAST)) {
    d = new LiddedDrink(d);
    }
    return d;
    }

    What if I’m experience frost-bite, and even the simple hot coffee is too much? Composition has got you covered here, too. Add a second sleeve to your drink.

    Hand-drawn image of a cup with a lid and two sleeves labeled “EXTRA HOT MOVING Drink”.
    Drink hotLiddedDrink = b.processOrder("extra-hot coffee", OrderType.HOT, OrderType.FAST);
    c.enjoy(hotLiddedDrink);
    System.out.println("...and if it's still too hot...");
    hotLiddedDrink = new SleevedDrink(hotLiddedDrink);
    hotLiddedDrink.grip()
    hotLiddedDrink.sip();

    What do all of these lids and sleeves mean for my software design?

    First lets make sure we understand what we are comparing. The first design showed a hierarchy of many types of cups. The second design showed components that could be assembled into different types of cups.

    The design based on a hierarchy has some disadvantages:

    • its impossible to change your mind about the type of cup later.
    • the logic the barista followed in processOrder is more complex.
    • the logic for handling a hot drink is implemented in multiple places, both HotCup and HotLidded cup. Or was it LiddedHotCup?

    The design based on composition had several advantages:

    • The design is flexible. both the customer and the barista could build out the perfect drink.
    • The barista’s steps in processOrder were, therefore, much simpler.
    • You could even add two sleeves, which was not possible with the hierarchical design.
    • The manager could just count cups, rather than guessing which customers need which kind of customization.
    • The logic for sleeves is in one place, the SleevedDrink.

    Both designs solved the problem, providing protection for your hands and your shirt, while letting you grip and sip.

    This example is a metaphor, and somebody could say completely contrived. Yet it shows the trade-offs involved when you think about how to break up your code.

    There are trade-offs for sure, and you can tell which side I lean towards.

    Composable design makes your software simpler and more adaptable. Your code becomes simpler. You avoid duplication. Your software can adapt to situations you may not have imagined up front.

    Consider using a composable design, and see how simple and adaptable your software can be.

    You can find the simulation source code on GitHub.

  • How to Solve the Double Standard of Software Estimates

    The double standard exists. There is an antidote.

    Photo by Pixabay

    Recently, I had some repairs done to my car.

    “There’s a noise coming from the rear wheels. The faster I go, the higher frequency of the sound. I think it’s on the left side.”

    Ok, that sounds like a rotational noise. We can take a look — our diagnostic fee is $259. We will apply that to any repairs based on what we find. We will call you with what we find and confirm how to proceed.

    My family had some home remodeling done last year to update the roof and siding on our house.

    Here is our estimate for material and labor, based on what we can see from the outside. As we get into the work, we may find other work you need. We will call you each time with the details and let you know how much more it will be.

    In both cases, I was the stakeholder. The artisans were my engineers. Both experiences left me thinking about what I can learn from these disciplines to apply to software engineering.

    I would receive a call when they found something new. As a stakeholder, I would be informed of the details as they emerged. The artisans kept the time to surprise to a minimum.

    Each experience was matter-of-fact about the work. New tasks were identified, which needed to be completed and paid. I knew the original estimate of time and money. Yet, I still had to wait and pay for the extra result.

    The workers could not predict the new work from the outside. It wasn’t until taking things apart and digging into the details that they could identify all the work. Along the way, I would get frequent calls to update me on the current status.

    When new work did emerge, the artisans adjusted timelines. Working late was not an option for safety reasons. The workers treated weekends the same way. Working faster did not mean anything. Cutting scope meant accepting an unacceptable level of quality.

    My experience with stakeholders of software teams has been a complete contrast to what I saw with the trades:

    • immediate negotiations to bring the time and cost down
    • a strong desire to control everything upfront, before taking things apart
    • an emotional reaction when the team does notify stakeholders of any changes.

    Stakeholders can learn from their interactions with the trades. Apply that thinking to your Engineering teams. This results in trust, better relationships, more creativity and a high-quality product.

    Use Meaningful Words

    Engineers have to control the narrative when it comes to estimates. The first step involves actively setting expectations. Along with that, explaining the words you are using becomes crucial.

    deadline is a point in time that, if missed, will result in dire consequences.

    Deadlines are standard when there is an expensive next step. For example, newspapers (when they were printed) needed to be set and run on the presses. Likewise, physical items need to be manufactured after they are designed.

    A deadline without consequences is meaningless. Using artificial deadlines adds tension and erodes trust. Both the date and the consequences have to make sense.

    Deadlines do make sense when you need to coordinate with somebody else’s schedule.

    commitment is a promise that something will happen.

    Commitments can only come from those involved in doing the work. Commitments are binding. Commitments are obligations that you take on wholeheartedly.

    An estimate is an informed guess.

    Based on some prior experience, an estimate is a guess about how long a piece of work will take.

    When you want to get some work done on your house, you’ll get an estimate. The workers will tell you how much it will cost and how long the job will take. They come to this based on the size and shape of your roof and their experience working on similar roofs.

    schedule is a list of intended events that will happen.

    Estimates will contribute to a schedule. Other events outside of this work will also contribute to the plan. Plan is a synonym for schedule. A good plan assumes that you’ve listed out everything that will take up your time.

    Finally, a forecast is the rate of executing against an estimate. A forecast uses the trends from recent history to predict how things will go. A forecast is an informed estimate, backed by data about completed work.

    Given what I know now, this is my estimate. This is a guess. It’s not a forecast based on a trend, and it’s not a commitment or a promise that we will hit a certain date. As we learn more, I will adjust the estimate and keep you updated.

    The biggest confusion in software development happens when estimates turn into deadlines without everyone involved. Use the specific term to explain what you are talking about. Contrast that to that other terms that you are not using.

    Set and Manage Expectations

    Engineering teams can apply these critical lessons:

    • explain what
    • set the expectation that we will learn more after we get started
    • come to terms — explain the difference between an estimate, a forecast and a commitment
    • immediately share what changes along the way
    • hold yourself to safety and quality standards.

    Use analogies your Stakeholders understand.

    When you are talking to someone in Finance, or the CEO for that matter, compare software estimates to a financial forecast. Explain that, like the companies annual plan, an estimate cannot double as a prediction. As you encounter more information, you will update the plan and inform your stakeholders.

    When you are talking to some in Sales, compare it to the sales targets. These are goals, informed by the market, that will direct the behavior of the sales team. And the sales team will work as hard as they can to reach this goal. When it is set, however, a sales target does not represent the final sales number you will get at the end of the period.

    When you’re talking to Marketing or Operations, contrast the custom innovative work you are doing with the repetitive task-based work of the agencies that they work with.

    Cut through the double standard. Explain the terms you use, what each word means. Contrast your terms with what it does not mean. Use analogies to other departments, and to common trades, to drive your point home.

    Your actions are the antidote to the double standard.