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
    1
    ·
    19 hours 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
        ·
        16 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
          ·
          9 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
            ·
            2 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.