Author: milo.todorovich@gmail.com

  • How I finished two graduate programs with a 3.95+ GPA without abandoning my family

    It can be done. Here are the specific practices that made it possible.

    I finished a Master’s of Science in Software Engineering at DePaul University with a 3.95 GPA. Later, I finished my PhD coursework in Organizational Psychology at Liberty University with a 3.97. Both while working full-time as a software engineer. Both while raising a family.

    Man balancing work with school schedule.
    Photo by Mina Rad on Unsplash

    This isn’t a story about talent or grinding through on willpower. It’s about a system. Ten specific practices that made it possible to sustain a multi-year academic commitment without sacrificing my career or disappearing from my family’s life.


    It was the spring of 2008. I was in the third year of a contract stint with a great team and in the 13th year of my professional career developing software. Great people, interesting work, new technologies. And this overwhelming feeling: I was in a rut.

    My wife and two young daughters had already gone overseas to spend the summer in Europe. I was going to join them in a few weeks. All I could think about was how my work had become mechanical.

    I called a trusted colleague to ask if he’d ever been through a low point. He turned me on to a friend of his who was becoming a career coach. So I had a conversation with her that didn’t go as either of us had imagined. I thought she was going to give me advice that would help me reboot my career. She thought she would be getting a new client. Instead, after our conversation, I began searching for graduate programs.

    I attended an open house at a local school and saw that they offered an evening program that would fit well with my job. I could earn a Master’s in Software Engineering while working as a software engineer. And graduate in as little as two years. I did some basic budgeting math, then talked with my wife. And together we decided that this was something to pursue.

    Here’s exactly how we did it.

    Twice.


    1. I knew why I was doing this.

    A rut is a signal. It means you’ve outgrown where you are, and you haven’t yet found where you’re going.

    Graduate school wasn’t a career move. At least not initially. It was a way to turn the learning back on. It gave me new material every week that I could connect to the work I was already doing. That tight loop of learning something new and immediately applying it had gone quiet, and I needed that back.

    If you’re going to sustain a multi-year commitment on top of a full-time job and a family, you need a reason that runs deeper than “it’ll look good on my resume.” When the semester gets hard, and it will, shallow reasons won’t hold.

    My most difficult times were during the warm parts of the year. My wife would be outside playing with our children, while I was inside watching lectures and completing assignments. Knowing that I was learning for a better future kept me going. It also gave me the focus I needed to finish the tasks for that day, so that I could spend some time with my family.

    2. I brought my wife into the commitment.

    This was not a solo decision. It couldn’t be.

    Graduate school while working full-time doesn’t just cost you time. It costs your family time. Evenings spent studying instead of being present. Weekends with a textbook instead of at the park. Going it alone means your spouse will pick up a lot they didn’t sign up for.

    My wife and I sat down, and we both laid it out honestly. Here’s what this will take. Here’s the weekly time commitment, and the little left over for family time. Here’s how long it will last. And here’s why we both think it’s worth it.

    She didn’t just agree. She committed. When your partner is committed, they’re not tolerating your absence, they’re actively holding the line with you. That distinction matters.

    Towards the end of my master’s program, my wife got pregnant. The first few months were full days of “morning” sickness. One night, when I had an exam scheduled, my wife was getting so dehydrated that she could not stop dry heaving. I called her and suggested that I skip the exam and come home to take care of her. She insisted she would be fine and that I should go take the exam. So I called my sister-in-law to drive my wife to the ER, and I caught up with them after I finished.

    That was her commitment in action. She wasn’t just tolerating the plan. She was protecting it, even when she was the one who needed help.

    My wife’s commitment really shined over those two years. She took the full load of spending time with our kids and managing the household. She cheered me on along the way. Together, we counted down the months until we could spend more time as a family.

    3. I did a time audit.

    Money was one constraint. Time was the harder one.

    I tracked how I spent my hours for a full week. Not a rough sense of it, but a detailed list of what I was doing. Work. Commute. Family time. Meals. TV. Sleep. All of it.

    What I found was what most people find. There was more recoverable time than I expected. Not huge blocks. But small pockets I’d been filling with things that weren’t moving me forward.

    I cut TV completely. That one stuck, and I haven’t gone back to watching TV since. I limited how much news I was reading. And I eliminated side projects. I wasn’t going to make up extra hours. Each day was still 24 hours long. But I could be more deliberate about how I used them.

    The time audit didn’t give me more time. It gave me a clear understanding about where my time was already going and permission to redirect it.

    Here’s how that worked out:

    • Work and commute: 50 hours per week
    • Class time: 12 hours per week
    • Study time: 24 hours per week

    When I factored in sleep, that left 2–3 hours per day of unaccounted time.

    4. We audited our budget to buy back family time.

    We looked at what we were spending, and where our money was going. We figured out where we could pull back. No new cars or home improvements during this period. Less eating out.

    Then we made the move that most people skip. Instead of pocketing the savings, we used them to buy back time.

    For those two years, we hired help. Lawn service for $40 per week. A maid to clean the house for $80 per week. For $120 per week, we got 6 hours of collective time back. This was well worth it.

    The goal was simple: my family should feel my presence, not my absence. Graduate school was going to take evenings and large chunks of our weekends. So I had to protect the time that remained and make it count. I couldn’t be everywhere. But I could make sure that when I was with my family, I was actually with them. Counting chores as “family time” would have been a lie.

    5. I built a flexible schedule.

    Rigid schedules break. Life with a family and a full-time job doesn’t follow a perfect calendar. Kids get sick. Work emergencies happen. You get sick yourself.

    I built a weekly rhythm instead of a daily routine. I knew how many hours I needed for coursework each week. I had preferred blocks: early mornings, late evenings after the kids were in bed, and big chunks of the weekend. But I kept them flexible.

    The key was having a target number of weekly hours, not a rigid daily schedule. Flexibility didn’t mean I had no plan. It meant I had a plan that could absorb the surprises of real life without falling apart.

    If Tuesday night blew up because of a work deadline, I shifted study time to Thursday. If a Saturday opened up because plans changed, I used it.

    6. I was honest about what I could prioritize.

    I could not give 100% to everything at the same time. I tried to tell myself I could. It didn’t work out.

    My general order of priorities in life is: Family. Health. Work. Learning. But during those two years, the real order was: Work. School. Family. I ignored my personal health. I put on weight because I stopped exercising. I didn’t go to after-work events. My wife represented our family at kids’ birthday parties and extended family gatherings when I couldn’t be there.

    That was the trade-off. I didn’t like it, but I was honest about it. Pretending I could do everything equally well would have just meant doing everything poorly.

    And then there were the disruptions I didn’t plan for. I started my master’s program in 2008, and in 2009 my employer cut the team citing the recession. I started my PhD in 2023, and my employer downsized due to budget issues a week after I started classes. I was looking for new work during both programs.

    Both times, my wife and I evaluated together. When I was looking for work a week into my PhD, we decided to finish the semester. I had already paid for it. We would reassess at the end of the semester. I found a new job before the semester was over.

    The system held. But it held because I was honest about what had to give and what couldn’t.

    What you choose to sacrifice will be different from what I chose. But you will choose something. Be honest about it up front. It should align with your primary values.

    7. I ramped up into the program.

    I didn’t start at full speed. That would have been a shock to both me and my family.

    My first semester, I took one class. Just one. I wanted to feel the weight of it. How much reading. How much writing. How much of my evening it would take. How it would affect my energy at work the next day.

    By the second semester, I knew the rhythm. I added a second class. By the third semester, I had a system. I knew exactly how to read a syllabus on day one and map out the entire semester’s workload before the first assignment was due.

    Ramping up gradually let me build the habit without breaking the system. By the time I was at full load, it felt sustainable, not survivable.

    8. I kept my timeline front and center.

    Two years. That was the window. Not “someday.” Not “as long as it takes.” Two years.

    I printed out the program timeline and kept it nearby so I’d see it every day. Every completed course was a visible step forward. Every semester was a countdown.

    At the end of each semester, I would pull out the schedule, and put a checkmark and a line through the completed courses. Seeing the timeline made it real. Checking off each class made the progress visible.

    When you’re grinding through a Tuesday night reading assignment after a full day of work, “two years” can feel infinite. But a timeline that shows you’re 40% done, then 60%, then 80% changes your psychology. You stop asking “is this worth it?” and start thinking “I’m almost there.”

    I used the same approach for my PhD coursework. It worked both times. The timeline turned endurance into momentum.

    9. I did the work every day.

    There’s no shortcut section in this article. No life hack. No secret.

    But there is a method. Keeping up each week was the key to having a good semester. The structure stayed the same: reading, engaging with my notes, and staying on schedule with the lectures. A little each day built towards the week. Each completed week built towards the semester.

    I read the material. I did the assignments. I wrote the papers. I studied for the exams. I showed up to every class, even when I was running on four hours of sleep.

    Some weeks were smooth. Some weeks, I was writing a paper at midnight after putting the kids to bed, knowing I had a 9am standup the next morning. The schedule bent but didn’t break because the systems I’d built in steps 1 through 8 were holding.

    The GPA wasn’t the goal. The goal was to learn as much as I could from every class. The GPA was a side effect of engaging with the material daily instead of cramming before exams.

    10. We celebrated each step.

    Not just graduation. Every semester.

    One evening we came home late after visiting my parents. I had an exam the day before, and grades were supposed to be posted that evening. I went to my office while my family was still settling in, saw my grade, and let out a big “Woohoo!!!”

    As I came out from my home office, I saw my wife and kids standing still, not sure of what was going on. I explained that I got a better grade than I thought, and that I was happy with the test result. And then they joined into the celebration, too.

    This matters more than you would think. A multi-year commitment without milestones feels like a death march. But when you stop every few months to look back and say “we did that” together, it recharges you for the next stretch.

    I said “we” because it was never just me. My wife carried the weight of the household so I could carry my course load. The celebration belonged to both of us.


    When I finally got my diploma, it wasn’t just a degree. It was proof that a deliberate, daily commitment to something bigger than yourself actually works. Not talent. Not luck. 

    A system. Ten practices. Applied consistently. Over months and years.

    It can be done. I did it twice.

  • Walking Skeletons in Software Lead to Amazing Results

    Four weeks into a project, we had automated 70% of our order approvals. Sounds great. Except we could have automated 35% of them in five days, and we didn’t.

    That gap haunts me. Not because of the engineering. The engineering was fine.

    It haunts me because of the five people on the order processing team who spent three extra weeks doing work that a computer could have done for them. And the sellers who waited days for their products to reach buyers, products they had poured weeks of effort into creating.

    This is what happens when you build software from the inside out. You assemble each of the parts, then put them all together at the end.

    It works. It’s logical. And it delays value for everyone.

    There is a better way. Slice your features thin, end to end through every layer, and deliver them one at a time. You will ship value in days instead of weeks. You will reduce risk. And you will be amazed by what happens then.

    Cockburn calls this a walking skeleton. The Pragmatic Programmers call this style tracer bullets

    How do you just start thinking in this daily habit?

    One of the first things you can do is to armchair your features after you are done. When you have completed a feature, especially one that took longer than you expected, think about how else you could have rearranged the work, so that you could confidently share your work every day.

    It may seem like you are playing armchair quarterback when you go back after you are done. The work is already complete, and now you have hindsight as your companion and colleague. Nonetheless, doing this exercise will help you start to think in smaller slices. It will start to change the way you think.

    Learning this way is one of the fastest ways to master something new. Often called trial and error, we should think of it as trial and feedback.


    Order Approvals: Four Weeks or Five Days

    I was working with a marketplace for digital products. Once a purchase is approved, these products are available for immediate delivery. Sellers put sweat and tears into building these products. Weeks of effort distilled into something a buyer could access in seconds.

    That instant delivery created a fraud problem. Someone buys a product with a stolen credit card, downloads it immediately, and the seller loses the product and the revenue. Their sweat equity just evaporated.

    To fight this, the company put together a team to review every order before it was fulfilled.

    As the business grew, so did the team, the workload, and the bottleneck. Buyers were waiting a day or two for products they were expecting instantly.

    Buyers called the support line. Support escalated to the approval team. The approval team fell further behind. And sellers watched their hard work sit in limbo, waiting on a process they couldn’t see or control.

    We put together a project to automate the review. We identified three rules:

    1. Rule 1 handled 15% of orders. It was complicated.
    2. Rule 2 handled 35% of orders. It was straightforward.
    3. Rule 3 handled 20% of orders. The complexity was somewhere in between.

    Orders that didn’t match any rule would still go to human review. Together, the three rules would automate about 70%.

    The team estimated three weeks. It took four. And during those four weeks, nothing changed for the humans doing the reviews. They were working harder, the orders were climbing, and there was no relief in sight.

    After the project shipped, Phil and I sat down to dissect how it went. Here’s what the team actually built, in order:

    Rule 1 first, the most complicated and risky. Then Rule 2 and Rule 3. Then the workflow change to split the order list into “needs review” and “already approved.”

    This is bottom-up development. Build the parts, then assemble them. It’s logical. Developers think this way naturally. How can I build a system if I don’t have the parts?

    Phil and I, who worked together on the project, asked a different question. How could we have delivered value sooner?

    We came up with this approach:

    1. Split the order list into multiple views by status. Small work.
    2. Add the workflow hook, a no-op that sends every order to manual review, same as before. Nothing changes yet.
    3. Implement Rule 2. The simple one. 35% of orders now auto-approve.
    4. Implement Rule 3. Another 20%.
    5. Implement Rule 1. The hard one. The final 15%.

    Steps 1 and 2 would have taken about three days. Step 3, another two. Within five days, 35% of orders would have been automated.

    Five days to get 35% of sellers’ products into buyers’ hands without delay. Five days to lighten the load on customer support. Five days to give the order processing team some breathing room.

    Instead, they waited four weeks for our original approach to build all of it at once.

    The total effort is roughly the same either way. The difference is when value arrives.


    Image Upload: Ten Days of Silence vs. Two Days of Progress

    This example comes from a 3D space-planning application. Users designed and visualized physical spaces: floor layouts, furniture placement, signage, the works.

    One feature let users place custom images into their 3D scenes. Grabbing an image from the asset library was easy. Getting a new image into the library was not. The original workflow required uploading through a separate web app, processing it through another application to create a 3D asset, then reloading the entire scene. Fine for one or two images. Unworkable when a layout redesign involved hundreds of new assets each month.

    The team was asked to build a streamlined upload: trigger it from within the app, select an image, and have it appear in both the library and the scene immediately.

    They estimated 10 to 15 days. I asked when we could see parts of it working. The answer: in 10 to 15 days.

    Another all-or-nothing feature.

    I started asking questions. How are you approaching the work? What’s the architecture? What comes first? What comes second?

    The plan was for two developers to work “together”, with heavy air quotes. One would start at the database layer and build upward. The other would start at the UI and build downward. In about 7 to 8 days each, they’d meet in the middle and connect the pieces. That connection could take a day, or it could take a week.

    I pulled Brandon aside. We talked through an alternative approach using thin slices through all the layers.

    We found something we could demonstrate in about two days:

    1. Trigger the upload action from the UI.
    2. Hard-code a known image path from the user’s machine.
    3. Send the image to the server with no additional attributes.
    4. The server applies default processing, creates the 3D asset, and responds.
    5. On response, the app reloads the entire asset library, the same reload that happens at startup.

    Ugly? Yes. Hard-coded? Absolutely.

    But in two days, the entire pipeline was working end to end. Every slice after that replaced a hard-coded piece with a real one.


    Scene Loading: Three Months in the Dark

    Building a 3D application is like building any other software in some ways, and completely different in others. There’s a render loop that constantly redraws the scene. Separation of concerns and clean architecture still apply. But the visual nature of the work changes what “progress” looks like.

    When we rebuilt this application, one of the first features was scene loading. A space in 3D is made up of layers: the shell (walls, floor, ceiling), fixtures (counters, tables, built-ins), sections with detailed layouts, display areas, and signage.

    Loading and composing all of that took about three months to build.

    And when it was done, loading a large scene took several minutes, slower than the application it was replacing. The new system was worse.

    The team had worked incrementally, but their increments followed architectural layers. First, load all the data. Then convert it to domain models. Then download the 3D assets. Then render. Seeing anything on screen took over two months.

    Along the way, features were added to keep the user informed while they waited: gray-boxing, then color-boxing, then white-boxing. Scene loading was rewritten, not refactored, twice.

    After the project, I sat down with Marcus, the lead developer, to talk about what we learned and what we could have done differently. I had a hunch that “differently” might also mean “better.”

    A single thin slice could have been: get the data for the shell, convert it to the domain model, download the one asset, and render it on screen.

    Just the walls and floor and ceiling. Incomplete. Useless. Or is it?

    That slice touches every layer:

    • Data access
    • Domain model
    • 3D asset
    • Rendering

    Just enough of each layer for one thing. And once that one thing works, new possibilities open up.

    A feature called screenshot, which generates an image of the 3D scene, could have been developed independently once the shell was rendering. That work could have started weeks earlier.

    Performance would have been visible from slice one. Instead of building everything and then discovering it was slow, telemetry from the first slice would have shown where time was being spent. Was it data loading for fixtures? Asset loading for sections? We would have known early and adjusted early. We might have avoided a rewrite or two.

    Think about what performance means for a developer’s daily life. If loading a scene takes four minutes, and you restart the app every fifteen minutes to test your work, that’s two hours a day spent watching a loading screen. Two hours. Every day. For months.

    If performance had been addressed from the first slice, imagine that loading time was one minute instead of four. That’s ninety minutes back every day. Happier developers. Faster features. Less context switching.

    A better application for the end user.


    Store Locator: A Story Decomposed

    I was on a team building a retail e-commerce site. The product owner described a Store Locator feature:

    Users search by zip code or city and state. The five closest stores appear with address, phone number, and hours. A map shows the stores with zoom and pan. The regional manager’s name, phone, email, and photo are displayed. And if the user is logged in, pre-fill their address.

    That’s not one feature. That’s seven.

    My first instinct was to split it by layer: build the models and services in week one, then the UI in week two. This felt productive. Yet something felt off. I kept thinking.

    Will the models I build in week one survive contact with the UI? What do I tell my product owner when he asks for a demo? That I’m 40% done? What does 40% done look like? And if a teammate offered to help, can I hand off any work?

    The answers were “no” and “I don’t know.”

    Instead, I figured out how to slice it into thin features, each one end to end:

    1. Search by zip code. Display stores with address and hours.
    2. Add search by city and state.
    3. Add a static map to results.
    4. Add zoom, pan, and navigation to the map.
    5. Display the regional manager’s name, phone, and email.
    6. Display the regional manager’s photo.
    7. Pre-fill search when the user is logged in.

    After feature 1 shipped, I could have built features 2 through 7 in almost any order. Some could have been built in parallel. The product owner saw real progress after each one. At one point, he changed the order of the remaining work and dropped the idea of the regional manager’s photo. This was exactly the kind of feedback I wanted early, not late.

    Thin slices were lighter to carry. I could think about search without worrying about maps or managers. The mental stack stayed small. The wins came fast. The feedback was real.

    It’s not always easy to find the right slices. The key: what can you demonstrate to the user? If you can’t show it, it’s too thin. If it doesn’t go end to end through your system, it’s not a slice. It’s a layer.


    The Pattern

    Every one of these examples follows the same arc. My team built from the inside out, assembled the pieces at the end, and delivered value all at once, weeks or months after starting.

    Each alternative approach slices thin, delivers end to end from the first few days, and creates options that the original approach never had.

    The pattern is simple:

    1. Tell the story from the user’s perspective. What problem are you solving?
    2. For each part of the story, identify the simplest possible version.
    3. Map the parts to your software architecture.
    4. Implement a single thin strand through all the layers.
    5. Demonstrate your work. Ask for feedback. Adjust the plan.
    6. Implement the next strand.

    Three loops drive this forward:

    • A tight loop of developing and delivering a strand.
    • A medium loop of getting feedback and adjusting course.
    • An outer loop of deciding whether to keep going or stop.

    You don’t need to define “done” on day one. You set a direction. You implement the simplest version. You get feedback. Seeing the solution, you decide the next step. Seeing all the steps together, you decide whether to continue or stop.

    Think about a suspension bridge. The strength to support lanes of traffic over a mile of open water does not come from a single thick cable. It comes from many long, thin strands, all working together.

    Working in long, thin slices gives you the strength to keep your project above water.


    “But Wouldn’t It Be More Efficient To Just Build It All At Once?”

    I hear this one the most. And on the surface, it makes sense. If you already know what you’re building, why not just build it?

    Three reasons.

    First, you don’t know what you’re building. You think you do. But the moment users see working software, priorities shift. Features get reordered. Some get cut entirely.

    Every story in this article includes a moment where the team could have changed direction, if we had delivered something sooner. When you build it all at once, you’re betting that your understanding on day one is still correct on day twenty. It rarely is.

    Second, the “efficiency” of building everything at once is an illusion created by ignoring what happens to everyone else while you build.

    The order processing team worked overtime for three extra weeks. Developers stared at four-minute loading screens for months. Efficiency measured only in developer keystrokes misses the cost carried by everyone waiting for value that could have arrived sooner.

    Third, small batches surface bugs faster. When you deliver one slice and something breaks, you know exactly what changed. When you deliver four weeks of work and something breaks, debugging becomes archaeology.

    The DORA research is clear on this: teams that work in small batches have lower change failure rates and faster recovery times. The time you think you’re saving by batching gets eaten by the debugging you create.

    “I’ll Build The Full API While I’m Already Here”

    This is YAGNI dressed in work clothes. You Aren’t Gonna Need It.

    Building the full API feels efficient because you’re already in the code. You already have context. Why not build the other three endpoints while you’re at it?

    Because every endpoint you build before it’s needed is a bet. You’re betting that the shape of the API won’t change when real users arrive. You’re betting that the time spent building, testing, and maintaining those endpoints won’t be wasted. And you’re betting that nothing more valuable could have been built with that same time.

    Martin Fowler breaks the cost into three parts: the cost of building it, the cost of delaying other work, and the cost of carrying the extra complexity. All three work against you.

    Build the one endpoint the current slice requires. Make it clean. Make it tested. Ship it. When the next slice needs another endpoint, build that one. Your API will end up shaped by real demand instead of speculation. And every endpoint in it will be there because someone actually needed it.

    “It’s Useless If I Hardcode That Value”

    In the image upload example, we hard-coded a known file path. It felt wrong. It felt like a shortcut that wouldn’t count.

    It counted.

    That hard-coded slice proved the entire pipeline worked end to end in two days. Server processing, asset creation, library reload, all connected and running. Every slice after that replaced one hard-coded piece with a real one. The file picker replaced the hard-coded path. Attribute editing replaced the defaults. Each replacement was small, tested, and shippable.

    The hard-coded version isn’t the product. It’s the skeleton that proves the product can stand up. Once the skeleton is standing, you add muscle one layer at a time.

    Without that skeleton, you’re two developers building from opposite ends for eight days, hoping everything connects at the end. With the skeleton in place, you have a working system on day two and a clear list of what to improve next.

    That useless hard-coded slice is the most useful thing you can build. It eliminates integration risk on day one instead of deferring it.

    “Who Would Even Want Such A Small Slice?”

    Your product owner. Your teammates. Your users.

    Every one of your stakeholders benefits from working in small slices. You validate assumptions. You will accelerate learning. You will unblock other work. And you’ll deliver real business outcomes sooner.

    The Amazing Part

    It’s not in any single example. It’s in the pattern that every example reveals, especially when you sit down afterward and rethink how the work could have gone.

    The amazing part happens after the walking skeleton ships. It’s all the options that open up. It’s who benefits.

    That first thin slice, the tracer bullet, puts every layer of the architecture into place. Once it’s standing, the work changes.

    A product owner who saw zip code search working on day three could decide that the map wasn’t worth building at all, saving her team a week of work on something nobody needed.

    Developers who rendered the shell of a 3D scene in week two could have started building screenshot, or tackled performance, instead of waiting three months to see anything on screen. Ninety minutes a day back in their lives. Fewer rewrites. Better software.

    Brandon, who was about to spend eight days building from the database up, had a working pipeline in two. Every slice after that was a clear, small improvement instead of a leap of faith.

    And that order processing team. Five people doing manual reviews while the system that would replace their work sat unfinished for three extra weeks. They could have had relief on day five. The sellers who poured weeks of effort into their products could have had buyers receiving them instantly, not sitting in a queue.

    That gap still haunts me. Not the engineering. The engineering was fine.

    What haunts me is the people. The time they spent waiting for value that could have arrived sooner. That’s the lesson that stuck: the cost of building from the inside out isn’t measured in code. It’s measured in the days that real people spend waiting for something better.

    Slice thin. Ship early. Watch who benefits.

    Deliver daily. One thin slice at a time.

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