• ☆ Yσɠƚԋσʂ ☆@lemmy.mlOP
    link
    fedilink
    arrow-up
    7
    ·
    1 month ago

    I’ve noticed that debugging tends to be more important in imperative languages than functional ones. With imperative style, you have a lot of implicit state that you need to know to figure out what actually happened. So, you end up having to go through the steps of building that state up before you can start figuring out what went wrong. On the other hand, the state is passed around explicitly with the functional paradigm, and you can typically figure out the problem by looking at the exact spot where the error occurred.

    My typical debugging workflow with Clojure is to just read the stack trace, go to the last function in it, and then see what it’s doing wrong. Very rarely do I find the need to start digging deeper. I think another aspect of it is having an interactive development workflow. When you’re running code as you’re developing it, you see problems pop up as you go and you can fix them before you move to the next step. This way you don’t end up in situations where you wrote a whole bunch of code that you haven’t run, and now you’re not sure if it all works the way you expected.

    • eldavi@lemmy.ml
      link
      fedilink
      English
      arrow-up
      6
      ·
      edit-2
      1 month ago

      With imperative style, you have a lot of implicit state that you need to know to figure out what actually happened. So, you end up having to go through the steps of building that state up before you can start figuring out what went wrong.

      i think i struggle with this part the most since i’m entirely self taught and relied on very old methods for writing my source since the educational material i used was the most common and freely available at the time i starting doing development work. i’ve learned that it was acceptably sufficient for the IT-based problems that i was trying to solve at the time i learned it and that legacy style has been keeping me at a disadvantage.

      if seen some of the newer style of debugging like the one you’re shared from the young fresh graduate developers who are lucky enough to be spared the slog of a over decade within “customer service” oriented side of the tech industry umbrella and it’s painfully evident to me how vastly superior it is compared to the old methods that i taught myself and it’s encouraged me to seek a degree to help me master them and my new job will make that degree free for me; which matters A LOT as an american considering the price tag it entails.

      • ☆ Yσɠƚԋσʂ ☆@lemmy.mlOP
        link
        fedilink
        arrow-up
        7
        ·
        1 month ago

        I find a good approach to getting better at programming is to reflect on the projects you’ve done and try to identify patterns that got you into trouble. Then you can try doing things differently next time, and eventually you end up settling on a style that works for you. At the end of the day it’s really just practice. The one key thing I’ve learned to focus on is reducing the operating context I need to have when reading the code. Once the context becomes too big to keep in your head, then trouble starts. So breaking things up aggressively into small components you can reason about in isolation tends to be the best way to write reliable code you can maintain over time.

        • SoyViking [he/him]@hexbear.net
          link
          fedilink
          English
          arrow-up
          2
          ·
          1 month ago

          So breaking things up aggressively into small components you can reason about in isolation tends to be the best way to write reliable code you can maintain over time.

          This is so true. Something that has really improved my coding has been having a linter that whines to me about assignment branch condition size. Compared with learning how to properly stub methods in tests it has helped me break tasks down into simple manageable chunks with little room for error.

          • ☆ Yσɠƚԋσʂ ☆@lemmy.mlOP
            link
            fedilink
            arrow-up
            2
            ·
            1 month ago

            I find it’s also helpful to explicitly think about high level flow in the code. There are typically two types of code in an application. There’s routing code that figures out where the payload needs to go, and then there’s the code that actually cares about the content of the payload. The routing code can be thought of as sort of a railway where you ship packages around. When a package gets to a destination then you pass it to the code that knows what do do with it.

            Nowadays, I really like to draw it out as a state machine before I start working on the code. When you just start coding, it’s very easy to focus on the happy path and then you end up having to start kludging handling of exceptional cases as they come up. When you sketch out the state machine, it forces you to consider the error cases up front. You don’t have to handle them right away, but the design should account for them at the very least. This is an excellent read about this approach https://shopify.engineering/17488160-why-developers-should-be-force-fed-state-machines