Context: Searching for a new senior level software development job over a 9 week period in summer 2025.

  • Focused mostly on data engineering and backend roles that are in-person or hybrid in the SF Bay Area.
  • Leads from recruiters on LinkedIn were much more likely to lead to interviews+offers.
  • The winning offer came through my personal network.
  • I mostly used Hiring.cafe for prospecting. They’re a scraper with an interface I didn’t hate.
    • squaresinger@lemmy.world
      link
      fedilink
      arrow-up
      2
      ·
      1 day ago

      The better option is to keep colors from the original input stream for the flows instead of making the flows an uniform color.

      In the input on the left you have pink, green and blue.

      Keep these colors throughout the graph.

      Except of the input, all of the other stages only ever split up and never merge, so keeping this single set of colors is enough.

      The other option would be to get rid of the “leads” stage, since it actually doesn’t change any state. All the other stages are an action that happens (e.g. “Applied” changes the state of the application from being just a lead to being an open application and it also filters out data for being e.g. abandoned). But the “leads” stage means the same thing as the first stage. So drop the “leads” stage and instead make flows go from all three input stages directly into “bad lead”, “abandoned” or “applied”.

      Combine both to get the best result.

        • squaresinger@lemmy.world
          link
          fedilink
          arrow-up
          1
          ·
          22 hours ago

          Don’t know if there’s a ready-made site for stuff like that, but it’s not hard to do.

          Here’s a quick and dirty AI generated piece of trash code as a proof of concept:

          # sankey_hiring_funnel_direct.py
          # Requires: plotly
          # Install: pip install plotly
          
          import plotly.graph_objects as go
          
          # Node labels (unique)
          labels = [
              "Network",            # 0
              "Hiring.cafe",        # 1
              "Abandoned Lead",     # 2
              "Applied",            # 3
              "Rejected",           # 4
              "No Response",        # 5
              "Screener",           # 6
              "Rejected by Screen", # 7
              "Full Round",         # 8
              "Rejected by Panel",  # 9
              "Offer",              #10
              "Accepted",           #11
              "Declined"            #12
          ]
          
          # Colors for the two source groups (consistent)
          network_color = "rgba(31,119,180,0.8)"      # blue-ish
          hiring_color  = "rgba(255,127,14,0.8)"      # orange-ish
          
          sources = []
          targets = []
          values  = []
          link_colors = []
          
          def add_link(src_idx, tgt_idx, val, color):
              sources.append(src_idx)
              targets.append(tgt_idx)
              values.append(val)
              link_colors.append(color)
          
          # Direct flows from Network and Hiring.cafe into Abandoned Lead and Applied
          add_link(0, 2, 1, network_color)    # Network -> Abandoned Lead (1)
          add_link(1, 2, 58, hiring_color)    # Hiring.cafe -> Abandoned Lead (58)
          add_link(0, 3, 11, network_color)   # Network -> Applied (11)
          add_link(1, 3, 70, hiring_color)    # Hiring.cafe -> Applied (70)
          
          # Applied -> Rejected, No Response, Screener (split by original group)
          add_link(3, 4, 5, network_color)    # Applied -> Rejected (network 5)
          add_link(3, 4, 40, hiring_color)    # Applied -> Rejected (hiring 40)
          add_link(3, 5, 3, network_color)    # Applied -> No Response (network 3)
          add_link(3, 5, 15, hiring_color)    # Applied -> No Response (hiring 15)
          add_link(3, 6, 4, network_color)    # Applied -> Screener (network 4)
          add_link(3, 6, 15, hiring_color)    # Applied -> Screener (hiring 15)
          
          # Screener -> Rejected by Screen, Full Round
          add_link(6, 7, 1, network_color)    # Screener -> Rejected by Screen (network 1)
          add_link(6, 7, 5, hiring_color)     # Screener -> Rejected by Screen (hiring 5)
          add_link(6, 8, 3, network_color)    # Screener -> Full Round (network 3)
          add_link(6, 8, 10, hiring_color)    # Screener -> Full Round (hiring 10)
          
          # Full Round -> Rejected by Panel, Offer
          add_link(8, 9, 1, network_color)    # Full Round -> Rejected by Panel (network 1)
          add_link(8, 9, 7, hiring_color)     # Full Round -> Rejected by Panel (hiring 7)
          add_link(8, 10, 2, network_color)   # Full Round -> Offer (network 2)
          add_link(8, 10, 3, hiring_color)    # Full Round -> Offer (hiring 3)
          
          # Offer -> Accepted, Declined
          add_link(10, 11, 1, network_color)  # Offer -> Accepted (network 1)
          add_link(10, 12, 1, network_color)  # Offer -> Declined (network 1)
          add_link(10, 12, 3, hiring_color)   # Offer -> Declined (hiring 3)
          
          # Sanity check
          assert len(sources) == len(targets) == len(values) == len(link_colors)
          
          # Node colors (visual guidance)
          node_colors = [
              "rgba(31,119,180,0.9)",   # Network
              "rgba(255,127,14,0.9)",   # Hiring.cafe
              "rgba(220,220,220,0.9)",  # Abandoned Lead
              "rgba(200,200,200,0.9)",  # Applied
              "rgba(220,180,180,0.9)",  # Rejected
              "rgba(200,200,220,0.9)",  # No Response
              "rgba(200,220,200,0.9)",  # Screener
              "rgba(255,200,200,0.9)",  # Rejected by Screen
              "rgba(210,210,255,0.9)",  # Full Round
              "rgba(240,200,220,0.9)",  # Rejected by Panel
              "rgba(200,255,200,0.9)",  # Offer
              "rgba(140,255,140,0.9)",  # Accepted
              "rgba(255,140,140,0.9)"   # Declined
          ]
          
          fig = go.Figure(data=[go.Sankey(
              node=dict(
                  pad=18,
                  thickness=18,
                  line=dict(color="black", width=0.5),
                  label=labels,
                  color=node_colors
              ),
              link=dict(
                  source=sources,
                  target=targets,
                  value=values,
                  color=link_colors,
                  hovertemplate='%{source.label} → %{target.label}: %{value}<extra></extra>'
              )
          )])
          
          fig.update_layout(
              title_text="Hiring funnel Sankey — direct source flows (no Leads node)",
              font_size=12,
              height=700,
              margin=dict(l=20, r=20, t=60, b=20)
          )
          
          fig.show()
          
          # To save as interactive HTML:
          # fig.write_html("sankey_hiring_funnel_direct.html", include_plotlyjs='cdn')
          

          Couldn’t be bothered to write this by hand for just an online comment. There’s enough that can be improved with this, but I think it’s ok to show how it can be done quite easily.

          • deegeese@sopuli.xyzOP
            link
            fedilink
            arrow-up
            1
            ·
            16 hours ago

            Thanks for sharing that. Seems like a promising vis technique but would work better with fewer final states than I used for a regular Sankey.

            • squaresinger@lemmy.world
              link
              fedilink
              arrow-up
              1
              ·
              9 hours ago

              I’m sure it’s possible to move the final states to the middle positions like you did. But I didn’t want to invest more time.