29 votes

Before you try to do something, make sure you can do nothing

7 comments

  1. [3]
    alxhghs
    Link
    This makes a lot of sense and intuitively I’ve done this for years but not in this order. I tend to skip the first step, make sure I have working code that “does nothing” and then I incrementally...

    This makes a lot of sense and intuitively I’ve done this for years but not in this order. I tend to skip the first step, make sure I have working code that “does nothing” and then I incrementally add the logic I care about. That’s a more realistic approach IMO than the idea of writing functions outside of the context of your larger program.

    10 votes
    1. skybrian
      Link Parent
      Bottom-up style isn't for everyone, but it does tend to result in more reusable pieces. You might also think of it as notebook or repl style. It's perhaps better for projects written from scratch....

      Bottom-up style isn't for everyone, but it does tend to result in more reusable pieces. You might also think of it as notebook or repl style. It's perhaps better for projects written from scratch.

      A danger is that maybe those pieces aren't what you actually need, if you don't have the right goal in mind. Sometimes it makes sense to plan from the top down and then implement from the bottom up.

      2 votes
    2. mxuribe
      Link Parent
      This article, and your comment reminded me of Dan Slimmon's blog post on the "Do Nothing" scripts: https://blog.danslimmon.com/2019/07/15/do-nothing-scripting-the-key-to-gradual-automation/ I did...

      This article, and your comment reminded me of Dan Slimmon's blog post on the "Do Nothing" scripts: https://blog.danslimmon.com/2019/07/15/do-nothing-scripting-the-key-to-gradual-automation/

      I did this once in a while years ago...but i guess i was too young to really understand how benefiicial this way of working can truly be. Then, after reading Dan's blog post, i now incorporate it as a basic practice almost always. This microsoft blog post simply reiterates the great tactic. :-)

      1 vote
  2. Meorawr
    Link
    I think this post just generally underscores the importance of trying to do things in incremental steps rather than diving head-first into a problem and getting roadblocked later on by an issue...

    I think this post just generally underscores the importance of trying to do things in incremental steps rather than diving head-first into a problem and getting roadblocked later on by an issue that can't trivially be extracted out and tested in isolation.

    As a rather convenient example I'll take what I worked on this morning. A colleague requested a tweak that would disable the integration of our software into the system service manager based off a development-only flag that can be persisted elsewhere.

    This change requires tweaks in a place that would - normally - take about 20 minutes to build and deploy. As the idea of pointless masochism doesn't spark joy in my eyes, I'd opted to break the task down into a few steps on a running test system.

    1. I confirmed precisely how I want to store this flag and the commands necessary to read and write it.
    2. I confirmed that the service manager can execute a script that reads a static piece of data into its environment.
    3. I confirmed that I can disable an arbitrary service based off said static data.
    4. I connected the script from step 2 to the commands from step 1 and re-confirmed the previous two things still worked as expected, except now backed by a dynamic flag.

    After all that I was reasonably confident my proposed change would work and integrated it into the source tree proper, and shortly afterward kicked off that build/deploy cycle. The changes ultimately worked on that first deployment precisely as expected with no surprises at all.

    Had I not taken the incremental approach then things would have been far more painful; there were plenty of depressingly simple mistakes I'd made throughout those steps that took seconds to fix but would have necessitated multiple redeployments.

    Contrast this to a separate task that a colleague recently worked on.

    The story here would be that they were to integrate support for an encrypted network link in a piece of client software of ours. They took the approach of largely just hammering this right into the software as one giant amorphous blob of a changeset that seemingly touched north of 30 files.

    The colleague however during this ran into an issue that they had trouble diagnosing - seemingly, when using the encrypted link things would hang somewhere and no further exchanges of messages were occuring.

    When I ended up trying to debug this issue myself and see what was going on I only very lightly looked at the changes they'd made to at least confirm some semblance of apparent correctness - and happy with that, I then promptly closed the tab and didn't look at their changes further. Even if I'd wanted to reproduce the issue with their changes, debugging the cause behind it would have been a nightmare given how deep some of these changes were cutting.

    So rather than join my colleague in their little tar pit I took a step back and did things incrementally from scratch. I wrote a separate minimal test application that verified I could fundamentally open an encrypted socket to the remote target, that I could get basic authentication of peers passing, and that I was able to perform a basic ping-like message exchange through our protocol.

    In contrast to the two weeks my colleague had spent on this task so far, my attempt to reproduce the issue with a minimal clean test case took about three hours to write from scratch and led to an identification on the issue - there was a bug on the other end of the socket where it got stuck waiting for data that'd never arrive. Ultimately I was able to have a fix in-place ready for testing about an hour after and deployed internally end of the day.

    What benefitted me the most was that I was able to isolate precisely at which step things were going wrong, and could make further changes around that one area to confirm or deny various theories on the underlying cause. Doing this in the context of the full changes my colleague had tried applying to the client software probably wouldn't have been impossible, but it would have taken considerably longer.

    8 votes
  3. MaoZedongers
    Link
    Working like that is so mind numbing it drives me crazy. I'll write tests before, and during writing the code, but I absolutely won't purposefully slow myself down and break my chain of thought on...

    Working like that is so mind numbing it drives me crazy.

    I'll write tests before, and during writing the code, but I absolutely won't purposefully slow myself down and break my chain of thought on the actual problem I'm solving just to fill in someone else's bullet point list of steps.

    5 votes
  4. [2]
    Nox_bee
    Link
    This sounds like the opinion of someone who's never had to work under time constraints. Write four separate methods and test them just for one function?

    This sounds like the opinion of someone who's never had to work under time constraints. Write four separate methods and test them just for one function?

    3 votes
    1. em-dash
      Link Parent
      Not really? The code from step 1 can be largely copypasted for step 4, especially if you know in advance that's what you're going to do. The intermediate steps are just "test your code as you're...

      Not really? The code from step 1 can be largely copypasted for step 4, especially if you know in advance that's what you're going to do. The intermediate steps are just "test your code as you're writing it", they're not additional reimplementations of the same thing.

      11 votes