Sherman On Software Latest

  • The Never Rewrite Podcast, Episode Ninety-Three, How to Put Work Down When Priorities Change

    The Never Rewrite Podcast, Episode Ninety-Three, How to Put Work Down When Priorities Change

    Priority changes rarely align with projects finishing, and changes often result in throwing away work in progress. In this episode, Isaac and I discuss different work philosophies and styles that will protect your work, minimize loss, and keep you productive.

    If you’ve ever stopped work on a project that was 90% complete and realized that it would never ship, this is the episode for you!h customer needs.

    Watch on YouTube or listen to it at Spotify, Apple Podcasts, or your favorite podcast app, and let us know if you have ever been involved in a rewrite. We would love to have you on the show to discuss your experience!

  • Rewrite Case Study – The Three Musketeers Don’t Deliver

    Rewrite Case Study – The Three Musketeers Don’t Deliver

    The Musketeer motto, “All for one, and one for all!” was a reminder that people are stronger when they work together.  Musketering works for problems as well; bringing them together makes them stronger and harder to resolve.  This is a case study where three unrelated problems: Data Latency, Data Quality, and UX were used to justify a full rewrite.

    The All For One mentality, resulted in a team working hard and delivering no customer value for 12 months.  The team was only able to deliver once they pivoted from Musketering and began attacking the problems separately.

    The Problem: Slow, Ugly, and Inconsistent Reports

    Our reporting pages had three problems:

    1. Data Latency – The data loaded extremely slowly.  Our biggest customers could wait up to 5 minutes for a report to load.
    2. Data Quality – The data was inconsistent with itself.  The campaign report might claim that 1,000 people opened an email while a list of everyone who opened the email would only have 995 people.
    3. UX – The reports were extremely outdated.  The UX hadn’t been refreshed in 8 years and the front end code was written in Ember, a dead frontend language.  The company’s overall frontend had been iteratively migrating to React for years, reports was one of the last remaining pieces.

    Addressing each issue would be a major undertaking, the team decided to tackle all 3 at once.

    Insight: The three issues have very little to do with each other.  The data being slow was unrelated to the data being inconsistent.  The outdated UX had nothing to do with the available data.

    Project Plan: New Data Stream, New Data Store, New UI – In 6 Weeks

    The plan called for:

    • All events to be published to kafka
    • All data to be stored in Snowflake
    • A new backend service to handle requests written in Java
    • New Reports in React

    At a high level the technical choices appeared reasonable.  None of the technology was new to the company.  Kafka was already in use and the new events wouldn’t add a significant amount of events onto the kafka stream.  There were several backend Java services.  More than 75% of the Ember pages had already been migrated to React.  Snowflake was available to some customers as an add-on feature.

    The choices became unreasonable when all of the moving pieces were to be completed in 6 weeks.

    All For One – This Is Blocked By That

    The UI couldn’t proceed until the Java service was set up.  The java service couldn’t be built until there was data in Snowflake.  Data couldn’t get in Snowflake until the events were published to Kafka.

    There was a natural order to the work; there was also a 6 week timeline.

    Could the UI begin development using dummy data?  Absolutely!  

    Would the UI require tons of rework when it came time to integrate with the Java service?  Absolutely!

    The tight coupling would rattle timelines up and down the stack as team members working in isolation made new discoveries.  Each group was working on the entire scope of the project – all of the UI, all of the java service, all of the Snowflake data.  The project was set up to deliver everything, or nothing – All For One, One Release For All!

    One For All!  After Nine Months, One Release

    Nine months into the six week project, the reports were ready to be released to customers.

    Data events were published to Kafka!  The Kafka stream was consumed by Snowflake!  The UI was in React!  There was a Java service acting as a middle layer between Snowflake and the React frontend.

    Customers in the beta group were generally happy with the new reporting experience.  All was well, until the AWS bill came in and was 20x more than expected.

    Tight Coupling And Tight Deadlines Left No Room For Changes

    It turned out that Snowflake was the wrong tool for the job.  This is not Snowflake’s fault, any more than a hammer is to blame for hammering a screw.

    The report needed unique ids in time series data, which isn’t something Snowflake is designed to support.  To get sets the developers added multiple array columns and did in memory operations to ensure that all of the array entries were unique.

    This was extremely computationally expensive.

    Because the project plan was supposed to deliver in 6 weeks the developers had to use an existing data store.  Because the project was late, there was no room to rethink the decision to use Snowflake.

    Divide And Conquer

    After 9 months of development, the new report had been released, and rolled back.  With Snowflake prohibitively expensive, the team needed a new plan.

    Remember, the plan had called for:

    • All events to be published to kafka
    • All data to be stored in Snowflake
    • A new backend service to handle requests written in Java
    • New Reports in React

    Without Snowflake there was no need to publish data to kafka, or a new Java service.

    The team switched from Musketering to Divide And Conquer.

    First, the team built new endpoints on the original service, using the original database.  It was slow, because the original system was slow.  It had inconsistencies, because the original system had inconsistencies.  The beautiful new UX was still beautiful.

    Second, the team made the reports efficient and fast by adding rollup tables to the original database.  This made the report fast as well as beautiful.

    Finally, the team worked on the data inconsistencies.  The inconsistencies were caused by race conditions and other concurrency issues.  There was a source of truth, and the team decided to mitigate the issue by periodically synching against the source of truth.

    From start to finish the pivot took 3 months.  The final version was fast, consistent, and beautiful.  The rollup tables and periodic sync increased database load by a small amount, well within the budget.

    Musketering Delays Delivery

    Bringing multiple problems together helps make the case for a rewrite more compelling.  It also creates unnecessary coupling within a project.  Changes create huge delays which discourages revising the plan.

    In this case almost everything written for the new reporting experience was thrown away.  Snowflake, because it was too expensive.  The Java Service and Kafka events because they existed to feed Snowflake.  Only the decision to rewrite 8 year old Ember reports in React turned out to be correct.

    The opposite of Musketering is Divide and Conquer.   By taking on one problem at a time the team was able to succeed in ⅓ of the time, with a much simpler and less expensive solution.

  • The Never Rewrite Podcast, Episode Ninety-Two, A Framework for Prioritizing Tech Debt Featuring Dustin Rea

    The Never Rewrite Podcast, Episode Ninety-Two, A Framework for Prioritizing Tech Debt Featuring Dustin Rea

    Dustin Rea joined Isaac and I to discuss frameworks for prioritizing tech debt. Dustin walks us through the 5 part ‘Jobs To Be Done’ framework which emphasizes the importance of aligning engineering and product teams with customer needs.

    If you’re struggling to tech debt and don’t know how to start, this is the episode for you!

    Watch on YouTube or listen to it at Spotify, Apple Podcasts, or your favorite podcast app, and let us know if you have ever been involved in a rewrite. We would love to have you on the show to discuss your experience!

  • State, Persistence, And Cold Restarts

    State, Persistence, And Cold Restarts

    In a SaaS, state exists in many places, some of them outside of your control.  If you don’t know where to look, you will never truly understand your system.

    State You Can Control

    As systems grow in complexity, state creeps into an ever growing number of places:

    • Databases
    • Caches
    • Running Software
    • Config Files
    • Shell scripts
    • Flat files

    Do your caches have state?  If they went down and it impacted anything other than performance, then they have state.

    Do your shell scripts have state?  If they reference specific customers then they certainly do.

    The list goes on.

    Some of your state has backups, like databases, source code, and some config.  Your only backups for running software and caches are more instances of running software and caches.

    State You Can’t Control

    State also creeps into things that aren’t software, and you can’t control:

    • Tribal knowledge
    • Manual Procedures
    • Anything in someone’s head that isn’t documented

    People are a critical, and critically overlooked, part of any complex system.  The most statefull people aren’t the programmers.  It’s the operations and support people that interact with customers that have the most state and least backup.

    Disaster Recovery and Cold Start

    A cold restart is the hardest kind of disaster recovery.  You have all new server instances, whatever is in source control, and whatever is in your backups.

    Things that live in people’s heads are even harder to recover.  Which repos are important, which services are supposed to run, what does the system even look like?

    Depending on your setup you may be ok, you may never recover.

    I was at GuaranteedRate when it acquired Discover’s Home Loan operations.  As part of the acquisition we got their mortgage software and their developers, but no state.  I watched the team of developers that created the system spend a year trying to do a cold restart.  They failed.

    Consider State Now, Before It Gets Away From You

    State grows organically with systems and keeping it under control requires effort.  Beyond the fear of disaster recovery, knowing where state exists in your system is key to maintenance and growth over time.

    Once you start looking for state in your systems, you’ll find it everywhere.

  • The Technical Problems You Should Solve At A Midsize SaaS

    The Technical Problems You Should Solve At A Midsize SaaS

    20 Things You Shouldn’t Build At A Midsize SaaS was about technical problems developers at a midsize SaaS shouldn’t try to solve.  Is midsize SaaS development all glue work?  Have all the problems been solved?

    Of course not.  Midsize SaaS is the garden of Refactoring, Scaling, and Performance.

    To make it sound fancy: The deep work for developers at a midsize SaaS is designing solutions for emergent architectural problems.

    A Midsize SaaS Has Different Problems Than A Startup SaaS

    Startups have unproven theories about what customers want.  They need to get features out as quickly as possible to test theory against reality.  Worrying about multiple data centers, global latency, or the performance of features customers can’t see, is a waste of time.

    At a startup you should write good code, find product market fit, and don’t worry about how the system will perform when you have 10,000 paying customers.

    Once you have thousands of paying customers, that’s when architectural gardening kicks in.

    How To Support What Customers Want

    The startup phase will leave you with a valuable product and an almost random set of assumptions.  You get to puzzle out the assumption, the reality, and choose solutions.

    If your systems are in the United States, and all of your customers are in the United States, you will have different architecture needs than if your customers are globally distributed.  Linear and exponential scaling produce different problems and require different solutions.

    You need to identify which problems you have, and iterate towards standard solutions.  Standard solutions are critical because it makes your competitive advantage, the differences that are valuable, shine through.  You can’t find the valuable unique differences when everything in your system is bespoke. 

    Conclusion

    The deep work at a midsize SaaS is identifying emerging problems and iterating towards solved solutions.  Pathfinding from wherever the startup phase has left you towards known destinations.  Moving towards known standard solutions makes it easier to find and improve valuable differentiators.  Building unique versions of everything makes everything harder without adding value to your customers.

  • The Never Rewrite Podcast, Episode Ninety-One, Pushing Pain to Users

    The Never Rewrite Podcast, Episode Ninety-One, Pushing Pain to Users

    his week, Isaac Askew and I work through the implications of externalizing costs in software development. What are the consequences of pushing pain to users? How do externalities impact customer experience and support? Is prioritizing features over user satisfaction ever a good tradeoff?

    Find out the answers to these and other burning questions in this week’s episode of NeverRewrite!

    Watch on YouTube or listen to it at Spotify, Apple Podcasts, or your favorite podcast app, and let us know if you have ever been involved in a rewrite. We would love to have you on the show to discuss your experience!

  • How To Say No As A Staff Engineer

    How To Say No As A Staff Engineer

    I say “no” to tech plans.  A lot of tech plans.  Most of the time, if you ask my opinion about a tech plan, I’m going to tell you not to do whatever you’re planning.  If product owners are involved the odds of me saying no go way up.  And yet, few people dread asking me about their plans.

    Staff Engineers can’t just say “no” and leave it at that.  The power of no comes with responsibility.  When I say “no”, I have to explain why, offer alternatives, and make myself available to work through emergent issues.  When product owners are involved it is my responsibility, as a Staff Engineer, to help separate the product goals, which are fine, from the solution, which got a “no”.

    Let’s break it down.

    Explain Why

    My expertise is in Scaling and Performance.  I say no when the solution won’t scale, won’t perform well, or has problematic edge cases.  Often, something that works fine for the average customer won’t work well for the largest 10% of customers.

    When I say no to a design that won’t scale, I speak to real numbers.  “That’s fine for a table with 1,000 rows.  We have customers with a million rows in that table.  They’ll be waiting minutes for a response from the backend.”

    Offer Alternatives

    Decades of experience doesn’t make me right, but it does give me a lot of experience to draw on.  There were specific problems in the design that caused me to say no.  Those problems are solvable.  The solutions may not be appealing, but they exist.

    If you have data centers all over the world, a central database full of global data is going to be a problem.  There are lots of alternatives depending on why you wanted global data in the first place.  Alternatives are a conversation, not prescription.

    Be Available For Emergent Issues

    Now that I’m part of the solution, I’m responsible for helping with emergent issues.  As the project continues, new constraints will emerge and new requirements will be discovered.  Some of the emergent issues are going to impact Scale and Performance.

    Things change and the old recommendations may become invalid.  Staff Engineering recommendations have to be living documents.

    Saying No Should Be More Work!

    It would be much easier to say “Looks good to me!”  If I pretend to be on board I’m no longer responsible.  I have sabotaged the asker, but they’ll be too busy fighting endless fires to realize that I could have stopped everything from going wrong.

    For Staff Engineers, saying no to a proposal comes with an implied offer to help.

  • Iteration Is Not A Speed, It’s A Journey

    Iteration Is Not A Speed, It’s A Journey

    Iterative development is faster, but iteration isn’t a speed, it’s a journey.  Apple iterates the iPhone annually.  So do car companies.  

    All iteration requires is to try and learn from the previous iteration.  Iterative development isn’t faster because iteration is fast, it’s faster because learning is part of the process.

    Sometimes the iterations in software are fast.  Sometimes they are glacially slow.

    As long as you are learning, and feeding the learning back into the next iteration, your software will improve.  You rarely only get one chance to release a piece of software.  Do your best, release, and learn.  Iteration isn’t a speed, it’s a journey towards better.

  • The Never Rewrite Podcast, Episode Ninety, The Hero and the Lifeguard

    The Never Rewrite Podcast, Episode Ninety, The Hero and the Lifeguard

    Isaac Askew and I explore the dynamics between two types of helpful developers: the ‘hero’ who swoops in to solve problems and the ‘lifeguard’ who encourages self-sufficiency. We dive deep into the tension that arises when these two personalities clash, and how to keep the peace between them. If you’ve ever struggled with helping too much or too little, this is a great episode for you!

    Watch on YouTube or listen to it at Spotify, Apple Podcasts, or your favorite podcast app, and let us know if you have ever been involved in a rewrite. We would love to have you on the show to discuss your experience!

  • Things I Learned By Helping To Program A Clavinet Clone

    Things I Learned By Helping To Program A Clavinet Clone

    Over the past few months I have helped my brother-in-law create a Clavinet Clone for musical keyboards.  Those of you who know me are undoubtedly laughing at the image of me creating a musical instrument because I have no rhythm, no timing, no ear, and generally musical ability of any kind.

    But, I do have over 20 years of programming experience, and I haven’t worked with someone who knows “just enough to be dangerous” in a long long time.  So I was surprised by some of the lesions.

    Source Control Is Not Intuitive

    Older source control systems like CVS were pretty simple, but didn’t support distributed development.  Git, on the other hand, is complicated.  Each time we did a handoff, I would make a branch, develop, and merge back to main.

    When I handed it back, my brother-in-law would copy the file, append “-new” and continue.  Every handover resulted in new copies of the files.  Sometimes entire directories would be copied and get “-old” added to the name.

    Branching, merging, and visual diffs may be second nature to full time developers, everyone else is going to need lesions.

    IDEs Will Spoil You

    The code is in Kontakt scripting language, which is an internally designed and developed scripting language.  I would do my development in Sublime Text, then switch over to the UI to compile and test.

    This got old after the 3rd syntax error.  A 15 second code-to-compile loop wasn’t bad back in 1992; now I just wanted syntax highlighting.

    Custom Scripting Languages Make Everything Harder

    Custom scripting languages make everything harder.  They make it harder on the implementer because they have to build and maintain a custom scripting language.  They make it harder on users because they have to learn a new language.

    A language with minimal functionality, limited documentation, and few examples.  The biggest time waster is the lack of a testing framework.

    With all that said, the Kontakt scripting language may not have been a mistake.  The same code and controls work on physical and virtual keyboards.  Getting code to work consistently on multiple hardware platforms is no small feat.

    If You Can’t Have Tests, Commit Continuously

    Continuous committing and diffs are a poor way to handle regressions, but they are better than nothing.  If you’re ever in this situation, make your commits as small and targeted as possible.  Regressions will still be frustratingly common and tedious, but at least you will be able to step back through your changes.

    Learning Things Sounds Great!

    You can buy the best sounding Clavinet Clone here!

    Full disclosure: I helped program it, it belongs to my brother-in-law, and I do not get a commission.