{
    "version": "https://jsonfeed.org/version/1",
    "title": "Swapcard Engineering",
    "home_page_url": "https://swapcard.engineering/",
    "description": "How we build Swapcard: architecture, infrastructure, product engineering and the people behind it.",
    "items": [
        {
            "id": "https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/",
            "content_html": "<p><a href=\"https://www.swapcard.com/blog/hosted-buyer-program-guide\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Hosted Buyer Programs (HBPs)</a> have become a must-have for many B2B events, connecting prequalified buyers with relevant exhibitors to foster meaningful business relationships. In this post, we unpack the algorithms and engineering that power this crucial feature.</p>\n<!-- -->\n<p>Beneath the smooth surface of a polished user experience lies a tough core engine. Algorithms, graphs, and optimization do the heavy lifting, with the traditional touch of <a href=\"https://www.youtube.com/watch?v=ry55--J4%5FVQ\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><em>let's fit this square into that circle</em></a> that characterizes any carefully designed theory when it meets real-world messiness. Still with us? Great. Let's dive in!</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"the-assignment-problem-events-edition\">The Assignment Problem: Events Edition<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#the-assignment-problem-events-edition\" class=\"hash-link\" aria-label=\"Direct link to The Assignment Problem: Events Edition\" title=\"Direct link to The Assignment Problem: Events Edition\" translate=\"no\">​</a></h2>\n<figure class=\"figure_CaPK\"><svg viewBox=\"0 0 720 340\" role=\"img\" aria-label=\"Buyers on the left are matched one-to-one with exhibitors on the right, among many possible pairings.\"><text x=\"48\" y=\"40\" class=\"mono_Qh06\">BUYERS</text><text x=\"672\" y=\"40\" text-anchor=\"end\" class=\"mono_Qh06\">EXHIBITORS</text><path d=\"M228,88 C348,88 372,88 492,88\" class=\"faintEdge_kjdv\"></path><path d=\"M228,88 C348,88 372,224 492,224\" class=\"faintEdge_kjdv\"></path><path d=\"M228,156 C348,156 372,156 492,156\" class=\"faintEdge_kjdv\"></path><path d=\"M228,156 C348,156 372,292 492,292\" class=\"faintEdge_kjdv\"></path><path d=\"M228,224 C348,224 372,224 492,224\" class=\"faintEdge_kjdv\"></path><path d=\"M228,292 C348,292 372,292 492,292\" class=\"faintEdge_kjdv\"></path><path d=\"M228,224 C348,224 372,88 492,88\" class=\"faintEdge_kjdv\"></path><path d=\"M228,292 C348,292 372,156 492,156\" class=\"faintEdge_kjdv\"></path><path d=\"M228,88 C348,88 372,156 492,156\" class=\"matchEdge_zSvF\"></path><path d=\"M228,156 C348,156 372,88 492,88\" class=\"matchEdge_zSvF\"></path><path d=\"M228,224 C348,224 372,292 492,292\" class=\"matchEdge_zSvF\"></path><path d=\"M228,292 C348,292 372,224 492,224\" class=\"matchEdge_zSvF\"></path><g transform=\"translate(48 64)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><circle cx=\"24\" cy=\"24\" r=\"12\" class=\"track4_STz0\"></circle><rect x=\"46\" y=\"16\" width=\"96\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"46\" y=\"28\" width=\"64\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"180\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g><g transform=\"translate(48 132)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><circle cx=\"24\" cy=\"24\" r=\"12\" class=\"track1_BAOW\"></circle><rect x=\"46\" y=\"16\" width=\"72\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"46\" y=\"28\" width=\"88\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"180\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g><g transform=\"translate(48 200)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><circle cx=\"24\" cy=\"24\" r=\"12\" class=\"track0_a3nw\"></circle><rect x=\"46\" y=\"16\" width=\"104\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"46\" y=\"28\" width=\"56\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"180\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g><g transform=\"translate(48 268)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><circle cx=\"24\" cy=\"24\" r=\"12\" class=\"track2_M6tT\"></circle><rect x=\"46\" y=\"16\" width=\"64\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"46\" y=\"28\" width=\"80\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"180\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g><g transform=\"translate(492 64)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><rect x=\"14\" y=\"21\" width=\"22\" height=\"16\" rx=\"2\" class=\"outline_Sfek\"></rect><path d=\"M11,21 L15,12 H35 L39,21 Z\" class=\"accentFill_Pl1K\"></path><rect x=\"48\" y=\"16\" width=\"80\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"48\" y=\"28\" width=\"44\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"0\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g><g transform=\"translate(492 132)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><rect x=\"14\" y=\"21\" width=\"22\" height=\"16\" rx=\"2\" class=\"outline_Sfek\"></rect><path d=\"M11,21 L15,12 H35 L39,21 Z\" class=\"accentFill_Pl1K\"></path><rect x=\"48\" y=\"16\" width=\"56\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"48\" y=\"28\" width=\"84\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"0\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g><g transform=\"translate(492 200)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><rect x=\"14\" y=\"21\" width=\"22\" height=\"16\" rx=\"2\" class=\"outline_Sfek\"></rect><path d=\"M11,21 L15,12 H35 L39,21 Z\" class=\"accentFill_Pl1K\"></path><rect x=\"48\" y=\"16\" width=\"88\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"48\" y=\"28\" width=\"52\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"0\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g><g transform=\"translate(492 268)\"><rect width=\"180\" height=\"48\" rx=\"12\" class=\"card_MXCK\"></rect><rect x=\"14\" y=\"21\" width=\"22\" height=\"16\" rx=\"2\" class=\"outline_Sfek\"></rect><path d=\"M11,21 L15,12 H35 L39,21 Z\" class=\"accentFill_Pl1K\"></path><rect x=\"48\" y=\"16\" width=\"64\" height=\"6\" rx=\"3\" class=\"barStrong_Q_PH\"></rect><rect x=\"48\" y=\"28\" width=\"76\" height=\"5\" rx=\"2.5\" class=\"bar_GqWN\"></rect><circle cx=\"0\" cy=\"24\" r=\"4\" class=\"accentFill_Pl1K\"></circle></g></svg></figure>\n<p>In 1962, <a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#ref1\" class=\"\">D. Gale and L. S. Shapley [1]</a> introduced the assignment problem, which laid the groundwork for understanding how to match two sets of agents based on preferences. The assignment problem is a fundamental combinatorial optimization problem that is still very active today, with fields of application ranging from <a href=\"https://en.wikipedia.org/wiki/Parcoursup\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">admission control in French universities</a> to <a href=\"https://youtu.be/djqhMvQe6JE\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">kidney transplants programs</a><a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#f1\" class=\"\">¹</a>.</p>\n<p>HBP essentially boils down to a sophisticated assignment problem: how do we optimally match a set of buyers with relevant exhibitors for meaningful, business-driving meetings? At first glance, it seems simple—pair each buyer to suitable sellers—but the reality is much more complex.</p>\n<p>Unlike textbook bipartite matching, live event scenarios introduce a mesh of interleaved constraints:</p>\n<p><strong>Physical</strong> Scheduling is limited by time (overlaps, event duration) and space (room capacity, access rights). HBP must avoid proposing impossible meetings.</p>\n<p><strong>Practical</strong> Attendees are busy. One cannot expect people to carefully grade every single potential meeting. We're limited to a few simple choices like “must meet”, “nice to meet”, or “do not meet”. For more nuance, HBP must extrapolate preferences using minimal user input.</p>\n<p><strong>Flexibility</strong> Real-world roles are fluid: a participant may buy milk and sell cheese; another might opt for a premium upgrade; journalists might squeeze in interviews… HBP must handle a hierarchy of possibly overlapping/conflicting conditions.</p>\n<p><strong>Fairness</strong> Purely optimal algorithmic solutions may inadvertently favor some, leaving others with little—HBP should nudge the system toward fair outcomes, so everyone benefits from the experience.</p>\n<p>Individually, these five facets (assignment plus the constraints) are manageable. The real challenge—and opportunity—is orchestrating all of them together, without converting your workflow into an unwieldy machine. That's why, instead of off-the-shelf modules, we designed our own custom HBP algorithm.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"beauty-is-in-the-eye-of-the-organizer\">Beauty is in the Eye of the Organizer<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#beauty-is-in-the-eye-of-the-organizer\" class=\"hash-link\" aria-label=\"Direct link to Beauty is in the Eye of the Organizer\" title=\"Direct link to Beauty is in the Eye of the Organizer\" translate=\"no\">​</a></h2>\n<p>A fundamental issue of assignment is the <a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#ref5\" class=\"\"><em>blocking pair</em> [5]</a>: Consider three participants, say Alice, Bob, and Charlie, with only one possible meeting slot. Alice wants Bob, Bob wants Charlie, Charlie wants Alice.</p>\n<figure class=\"figure_CaPK\"><svg viewBox=\"0 0 720 420\" role=\"img\" aria-label=\"Alice wants to meet Bob, Bob wants to meet Charlie, and Charlie wants to meet Alice.\"><defs><marker id=\"hbp-cycle-arrow\" viewBox=\"0 0 10 10\" refX=\"8.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"12\" markerHeight=\"12\" orient=\"auto-start-reverse\"><path d=\"M0,0 L10,5 L0,10 z\" class=\"inkFill_VyiV\"></path></marker></defs><path d=\"M222,266 L306,154\" class=\"edge_xWd2\" marker-end=\"url(#hbp-cycle-arrow)\"></path><path d=\"M414,154 L498,266\" class=\"edge_xWd2\" marker-end=\"url(#hbp-cycle-arrow)\"></path><path d=\"M492,332 L228,332\" class=\"edge_xWd2\" marker-end=\"url(#hbp-cycle-arrow)\"></path><text x=\"246\" y=\"198\" text-anchor=\"middle\" transform=\"rotate(-53 246 198)\" class=\"edgeLabel_bhts\">wants to meet</text><text x=\"474\" y=\"198\" text-anchor=\"middle\" transform=\"rotate(53 474 198)\" class=\"edgeLabel_bhts\">wants to meet</text><text x=\"360\" y=\"360\" text-anchor=\"middle\" class=\"edgeLabel_bhts\">wants to meet</text><g transform=\"translate(360 248)\" class=\"cycle_LM1v\"><path d=\"M-22,-6 A23,23 0 0 1 18,-14\"></path><path d=\"M22,6 A23,23 0 0 1 -18,14\"></path><path d=\"M12,-20 L19,-14 L11,-8\"></path><path d=\"M-12,20 L-19,14 L-11,8\"></path></g><g transform=\"translate(308 20)\"><rect width=\"104\" height=\"124\" rx=\"12\" class=\"card_MXCK\"></rect><path d=\"M0,12 a12,12 0 0 1 12,-12 h80 a12,12 0 0 1 12,12 v16 h-104 z\" class=\"inkFill_VyiV\"></path><rect x=\"40\" y=\"8\" width=\"24\" height=\"5\" rx=\"2.5\" class=\"paperFill_m4a7\"></rect><circle cx=\"52\" cy=\"60\" r=\"20\" class=\"track1_BAOW\"></circle><text x=\"52\" y=\"67\" text-anchor=\"middle\" class=\"initial_K_IW\">B</text><text x=\"52\" y=\"106\" text-anchor=\"middle\" class=\"name_NGlY\">Bob</text></g><g transform=\"translate(120 270)\"><rect width=\"104\" height=\"124\" rx=\"12\" class=\"card_MXCK\"></rect><path d=\"M0,12 a12,12 0 0 1 12,-12 h80 a12,12 0 0 1 12,12 v16 h-104 z\" class=\"inkFill_VyiV\"></path><rect x=\"40\" y=\"8\" width=\"24\" height=\"5\" rx=\"2.5\" class=\"paperFill_m4a7\"></rect><circle cx=\"52\" cy=\"60\" r=\"20\" class=\"track4_STz0\"></circle><text x=\"52\" y=\"67\" text-anchor=\"middle\" class=\"initial_K_IW\">A</text><text x=\"52\" y=\"106\" text-anchor=\"middle\" class=\"name_NGlY\">Alice</text></g><g transform=\"translate(496 270)\"><rect width=\"104\" height=\"124\" rx=\"12\" class=\"card_MXCK\"></rect><path d=\"M0,12 a12,12 0 0 1 12,-12 h80 a12,12 0 0 1 12,12 v16 h-104 z\" class=\"inkFill_VyiV\"></path><rect x=\"40\" y=\"8\" width=\"24\" height=\"5\" rx=\"2.5\" class=\"paperFill_m4a7\"></rect><circle cx=\"52\" cy=\"60\" r=\"20\" class=\"track0_a3nw\"></circle><text x=\"52\" y=\"67\" text-anchor=\"middle\" class=\"initial_K_IW\">C</text><text x=\"52\" y=\"106\" text-anchor=\"middle\" class=\"name_NGlY\">Charlie</text></g></svg></figure>\n<p><em>Which meeting shall we schedule?</em></p>\n<p>Set up Alice–Bob? Bob and Charlie will “block”: Bob wants Charlie, and Charlie has no meeting. Maybe Bob–Charlie then? Now Charlie and Alice block: Charlie seeks Alice, and Alice is left alone. Try Charlie–Alice? Alice and Bob block that.</p>\n<p>The cycle highlights a simple truth: every participant views meetings differently. Focusing only on their perspective creates conflict. Instead, each meeting should have a value set by the organizer, folding in individual preferences and the broader event context into one clear score. After all, it's the organizer who orchestrates the event, so the final decision should rest here. Theoretically, this <a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#ref2\" class=\"\">simplifies the problem [2]</a>; practically, it allows meetings to be ranked by a single score.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"one-score-to-rule-them-all\">One score to rule them all<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#one-score-to-rule-them-all\" class=\"hash-link\" aria-label=\"Direct link to One score to rule them all\" title=\"Direct link to One score to rule them all\" translate=\"no\">​</a></h2>\n<figure class=\"figure_CaPK\"><svg viewBox=\"0 0 720 360\" role=\"img\" aria-label=\"Reciprocity, priority, intensity and affinity, in precedence order, combine into a single meeting score.\"><text x=\"40\" y=\"44\" class=\"mono_Qh06\">PRECEDENCE ↓</text><g><path d=\"M236,95 C340,95 360,180 442,180\" class=\"faintEdgeSolid_FBz7\"></path><rect x=\"40\" y=\"70\" width=\"196\" height=\"50\" rx=\"12\" class=\"card_MXCK\"></rect><text x=\"56\" y=\"100\" class=\"mono_Qh06\">01</text><rect x=\"88\" y=\"88\" width=\"12\" height=\"12\" rx=\"3\" class=\"track0_a3nw\"></rect><text x=\"110\" y=\"100\" class=\"sans_zNuI\">Reciprocity</text></g><g><path d=\"M236,159 C340,159 360,180 442,180\" class=\"faintEdgeSolid_FBz7\"></path><rect x=\"40\" y=\"134\" width=\"196\" height=\"50\" rx=\"12\" class=\"card_MXCK\"></rect><text x=\"56\" y=\"164\" class=\"mono_Qh06\">02</text><rect x=\"88\" y=\"152\" width=\"12\" height=\"12\" rx=\"3\" class=\"track1_BAOW\"></rect><text x=\"110\" y=\"164\" class=\"sans_zNuI\">Priority</text></g><g><path d=\"M236,223 C340,223 360,180 442,180\" class=\"faintEdgeSolid_FBz7\"></path><rect x=\"40\" y=\"198\" width=\"196\" height=\"50\" rx=\"12\" class=\"card_MXCK\"></rect><text x=\"56\" y=\"228\" class=\"mono_Qh06\">03</text><rect x=\"88\" y=\"216\" width=\"12\" height=\"12\" rx=\"3\" class=\"track2_M6tT\"></rect><text x=\"110\" y=\"228\" class=\"sans_zNuI\">Intensity</text></g><g><path d=\"M236,287 C340,287 360,180 442,180\" class=\"faintEdgeSolid_FBz7\"></path><rect x=\"40\" y=\"262\" width=\"196\" height=\"50\" rx=\"12\" class=\"card_MXCK\"></rect><text x=\"56\" y=\"292\" class=\"mono_Qh06\">04</text><rect x=\"88\" y=\"280\" width=\"12\" height=\"12\" rx=\"3\" class=\"track4_STz0\"></rect><text x=\"110\" y=\"292\" class=\"sans_zNuI\">Affinity</text></g><circle cx=\"556\" cy=\"180\" r=\"100\" class=\"ringTrack_qCoj\"></circle><circle cx=\"556\" cy=\"180\" r=\"100\" class=\"trackStroke0_iIC1\" stroke-width=\"16\" fill=\"none\" stroke-dasharray=\"230.7610416728243 397.5574890451344\" stroke-dashoffset=\"0\" transform=\"rotate(-90 556 180)\"></circle><circle cx=\"556\" cy=\"180\" r=\"100\" class=\"trackStroke1_Cwm7\" stroke-width=\"16\" fill=\"none\" stroke-dasharray=\"167.92918860102844 460.3893421169302\" stroke-dashoffset=\"-238.7610416728243\" transform=\"rotate(-90 556 180)\"></circle><circle cx=\"556\" cy=\"180\" r=\"100\" class=\"trackStroke2_e_X4\" stroke-width=\"16\" fill=\"none\" stroke-dasharray=\"117.66370614359174 510.6548245743669\" stroke-dashoffset=\"-414.69023027385276\" transform=\"rotate(-90 556 180)\"></circle><circle cx=\"556\" cy=\"180\" r=\"100\" class=\"trackStroke4_EE88\" stroke-width=\"16\" fill=\"none\" stroke-dasharray=\"79.96459430051422 548.3539364174444\" stroke-dashoffset=\"-540.3539364174445\" transform=\"rotate(-90 556 180)\"></circle><text x=\"556\" y=\"202\" text-anchor=\"middle\" class=\"bigNumber_BLQG\">1</text><text x=\"556\" y=\"230\" text-anchor=\"middle\" class=\"mono_Qh06\">SCORE</text></svg></figure>\n<p>Our scoring function is designed to synthesize all relevant signals, blending them smartly. These factors include:</p>\n<p><strong>Reciprocity</strong> If Alice wants to meet Bob, and Bob reciprocates, the score goes up.</p>\n<p><strong>Priority</strong> Premium participants or sponsor status should boost meeting scores.</p>\n<p><strong>Intensity</strong> “Must meet” preferences trump mere interest.</p>\n<p><strong>Affinity</strong> A shared background increases the value.</p>\n<p>We structure signals in a precedence order: reciprocity outranks priority, which rules over intensity, which surpasses affinity. This ensures the most critical factors are always weighed first, with subtler signals retaining influence.</p>\n<p>Armed with these weighted scores, meeting choices can be justified. In Alice-Bob-Charlie example above, should the organizer pick Alice–Bob and face objections from Bob and Charlie, there's a transparent rationale: perhaps Alice paid for premium priority, or flagged Bob as “must have”, or shares greater professional affinity. Maybe Charlie explicitly opted out of meeting Bob, etc.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"one-meeting-at-a-time\">One meeting at a time<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#one-meeting-at-a-time\" class=\"hash-link\" aria-label=\"Direct link to One meeting at a time\" title=\"Direct link to One meeting at a time\" translate=\"no\">​</a></h2>\n<figure class=\"figure_CaPK\"><svg viewBox=\"0 0 720 360\" role=\"img\" aria-label=\"Candidate meetings, sorted by decreasing score, are placed one at a time into the event schedule.\"><defs><marker id=\"hbp-schedule-arrow\" viewBox=\"0 0 10 10\" refX=\"8.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"12\" markerHeight=\"12\" orient=\"auto-start-reverse\"><path d=\"M0,0 L10,5 L0,10 z\" class=\"accentFill_Pl1K\"></path></marker></defs><text x=\"40\" y=\"44\" class=\"mono_Qh06\">SORTED BY SCORE ↓</text><g transform=\"translate(40 68)\" opacity=\"1\"><rect width=\"220\" height=\"44\" rx=\"10\" class=\"cardActive_wild\"></rect><circle cx=\"22\" cy=\"22\" r=\"11\" class=\"track4_STz0\"></circle><circle cx=\"38\" cy=\"22\" r=\"11\" class=\"track1_BAOW ringed_PO0_\"></circle><text x=\"22\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">A</text><text x=\"38\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">B</text><rect x=\"64\" y=\"19\" width=\"136\" height=\"6\" rx=\"3\" class=\"bar_GqWN\"></rect><rect x=\"64\" y=\"19\" width=\"125.12\" height=\"6\" rx=\"3\" class=\"accentFill_Pl1K\"></rect></g><g transform=\"translate(40 124)\" opacity=\"0.84\"><rect width=\"220\" height=\"44\" rx=\"10\" class=\"card_MXCK\"></rect><circle cx=\"22\" cy=\"22\" r=\"11\" class=\"track0_a3nw\"></circle><circle cx=\"38\" cy=\"22\" r=\"11\" class=\"track2_M6tT ringed_PO0_\"></circle><text x=\"22\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">C</text><text x=\"38\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">D</text><rect x=\"64\" y=\"19\" width=\"136\" height=\"6\" rx=\"3\" class=\"bar_GqWN\"></rect><rect x=\"64\" y=\"19\" width=\"106.08\" height=\"6\" rx=\"3\" class=\"accentFill_Pl1K\"></rect></g><g transform=\"translate(40 180)\" opacity=\"0.6799999999999999\"><rect width=\"220\" height=\"44\" rx=\"10\" class=\"card_MXCK\"></rect><circle cx=\"22\" cy=\"22\" r=\"11\" class=\"track1_BAOW\"></circle><circle cx=\"38\" cy=\"22\" r=\"11\" class=\"track2_M6tT ringed_PO0_\"></circle><text x=\"22\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">B</text><text x=\"38\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">D</text><rect x=\"64\" y=\"19\" width=\"136\" height=\"6\" rx=\"3\" class=\"bar_GqWN\"></rect><rect x=\"64\" y=\"19\" width=\"87.04\" height=\"6\" rx=\"3\" class=\"accentFill_Pl1K\"></rect></g><g transform=\"translate(40 236)\" opacity=\"0.52\"><rect width=\"220\" height=\"44\" rx=\"10\" class=\"card_MXCK\"></rect><circle cx=\"22\" cy=\"22\" r=\"11\" class=\"track4_STz0\"></circle><circle cx=\"38\" cy=\"22\" r=\"11\" class=\"track0_a3nw ringed_PO0_\"></circle><text x=\"22\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">A</text><text x=\"38\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">C</text><rect x=\"64\" y=\"19\" width=\"136\" height=\"6\" rx=\"3\" class=\"bar_GqWN\"></rect><rect x=\"64\" y=\"19\" width=\"68\" height=\"6\" rx=\"3\" class=\"accentFill_Pl1K\"></rect></g><g transform=\"translate(40 292)\" opacity=\"0.36\"><rect width=\"220\" height=\"44\" rx=\"10\" class=\"card_MXCK\"></rect><circle cx=\"22\" cy=\"22\" r=\"11\" class=\"track0_a3nw\"></circle><circle cx=\"38\" cy=\"22\" r=\"11\" class=\"track1_BAOW ringed_PO0_\"></circle><text x=\"22\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">C</text><text x=\"38\" y=\"26\" text-anchor=\"middle\" class=\"initialSmall_eZeC\">B</text><rect x=\"64\" y=\"19\" width=\"136\" height=\"6\" rx=\"3\" class=\"bar_GqWN\"></rect><rect x=\"64\" y=\"19\" width=\"48.96\" height=\"6\" rx=\"3\" class=\"accentFill_Pl1K\"></rect></g><text x=\"332\" y=\"44\" class=\"mono_Qh06\">SCHEDULE</text><text x=\"340\" y=\"74\" class=\"monoSmall_q436\">09:00</text><text x=\"428\" y=\"74\" class=\"monoSmall_q436\">09:30</text><text x=\"516\" y=\"74\" class=\"monoSmall_q436\">10:00</text><text x=\"604\" y=\"74\" class=\"monoSmall_q436\">10:30</text><rect x=\"332\" y=\"84\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"420\" y=\"84\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"508\" y=\"84\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"596\" y=\"84\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"332\" y=\"144\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"420\" y=\"144\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"508\" y=\"144\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"596\" y=\"144\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"332\" y=\"204\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"420\" y=\"204\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"508\" y=\"204\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"596\" y=\"204\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"332\" y=\"264\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"420\" y=\"264\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"508\" y=\"264\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><rect x=\"596\" y=\"264\" width=\"88\" height=\"60\" class=\"cell_VP8y\"></rect><g transform=\"translate(337 149)\"><rect width=\"78\" height=\"50\" rx=\"8\" class=\"placed_Kyj9\"></rect><rect width=\"3\" height=\"50\" rx=\"1.5\" class=\"accentFill_Pl1K\"></rect><text x=\"12\" y=\"30\" class=\"monoInk_aDHq\">E·F</text></g><g transform=\"translate(513 89)\"><rect width=\"78\" height=\"50\" rx=\"8\" class=\"placed_Kyj9\"></rect><rect width=\"3\" height=\"50\" rx=\"1.5\" class=\"accentFill_Pl1K\"></rect><text x=\"12\" y=\"30\" class=\"monoInk_aDHq\">G·H</text></g><g transform=\"translate(425 269)\"><rect width=\"78\" height=\"50\" rx=\"8\" class=\"placed_Kyj9\"></rect><rect width=\"3\" height=\"50\" rx=\"1.5\" class=\"accentFill_Pl1K\"></rect><text x=\"12\" y=\"30\" class=\"monoInk_aDHq\">E·J</text></g><g transform=\"translate(601 209)\"><rect width=\"78\" height=\"50\" rx=\"8\" class=\"placed_Kyj9\"></rect><rect width=\"3\" height=\"50\" rx=\"1.5\" class=\"accentFill_Pl1K\"></rect><text x=\"12\" y=\"30\" class=\"monoInk_aDHq\">K·F</text></g><g transform=\"translate(513 209)\"><rect width=\"78\" height=\"50\" rx=\"8\" class=\"placed_Kyj9\"></rect><rect width=\"3\" height=\"50\" rx=\"1.5\" class=\"accentFill_Pl1K\"></rect><text x=\"12\" y=\"30\" class=\"monoInk_aDHq\">G·L</text></g><rect x=\"425\" y=\"89\" width=\"78\" height=\"50\" rx=\"8\" class=\"target_R6NI\"></rect><text x=\"437\" y=\"119\" class=\"monoAccent_woeC\">A·B</text><path d=\"M262,90 C304,90 324,154 422,114\" class=\"moveEdge_JxdS\" marker-end=\"url(#hbp-schedule-arrow)\"></path></svg></figure>\n<p>A scoring function in hand, scheduling unfolds through a simple workflow.</p>\n<div class=\"language-text codeBlockContainer_mQmQ theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_t_Hd\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_RMoD thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_AclH\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Meetings ← All compatible meetings</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">for meeting ∈ Sorted(Meetings, decreasing)</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    if IsValid(meeting)</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        Instantiate(meeting)</span><br></div></code></pre></div></div>\n<p><em>Hosted Buyer Program core loop</em></p>\n<p>Step-by-step, the process works as follows:</p>\n<p><strong>Preselect compatible meetings</strong> All conditions set by the organizer are reviewed; meetings explicitly disallowed by participants or those breaking condition options (e.g., existing contacts, colleagues) are excluded.</p>\n<p><strong>Sort by decreasing scores</strong> This is where the magic happens. When we process a meeting, we know that it is more important than any meeting not processed yet, so we can fully focus on it.</p>\n<p><strong>Validate meeting</strong> Compatibility isn't enough. Schedule checks, load levels, room logistics, and dynamic issues (such as not meeting the same company twice) are all considered.</p>\n<p><strong>Instantiate the meeting</strong> Choose the best time and place for that meeting: Avoid gaps, minimize commute, prefer dedicated spaces like booths… Tag the meeting with its score, including some basic affinity details.</p>\n<p><strong>Repeat</strong> Continue until no further meetings can be placed, or participant quotas are reached.</p>\n<p>Performance is a central theme: our current system can process thousands of participants and hundreds of thousands of meeting possibilities in mere seconds.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"beware-of-unicorns\">Beware of Unicorns<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#beware-of-unicorns\" class=\"hash-link\" aria-label=\"Direct link to Beware of Unicorns\" title=\"Direct link to Beware of Unicorns\" translate=\"no\">​</a></h2>\n<figure class=\"figure_CaPK\"><svg viewBox=\"0 0 720 360\" role=\"img\" aria-label=\"A warning sign with a unicorn: Beware of Unicorns.\"><g transform=\"translate(64 66) rotate(-6)\"><rect width=\"105.39999999999999\" height=\"30\" rx=\"15\" class=\"chip_C2V8\"></rect><text x=\"14\" y=\"20\" class=\"monoMuted_RkK5\">“perfect”</text></g><g transform=\"translate(528 52) rotate(5)\"><rect width=\"131.2\" height=\"30\" rx=\"15\" class=\"chip_C2V8\"></rect><text x=\"14\" y=\"20\" class=\"monoMuted_RkK5\">“AI-powered”</text></g><g transform=\"translate(28 304) rotate(-3)\"><rect width=\"260.2\" height=\"30\" rx=\"15\" class=\"chip_C2V8\"></rect><text x=\"14\" y=\"20\" class=\"monoMuted_RkK5\">“machine learning-enhanced”</text></g><path transform=\"translate(200 120)\" d=\"M0,-9 Q0,0 9,0 Q0,0 0,9 Q0,0 -9,0 Q0,0 0,-9 z\" class=\"accentFill_Pl1K\"></path><path transform=\"translate(560 124)\" d=\"M0,-6 Q0,0 6,0 Q0,0 0,6 Q0,0 -6,0 Q0,0 0,-6 z\" class=\"accentFill_Pl1K\"></path><path transform=\"translate(124 236)\" d=\"M0,-6 Q0,0 6,0 Q0,0 0,6 Q0,0 -6,0 Q0,0 0,-6 z\" class=\"accentFill_Pl1K\"></path><path transform=\"translate(640 236)\" d=\"M0,-8 Q0,0 8,0 Q0,0 0,8 Q0,0 -8,0 Q0,0 0,-8 z\" class=\"accentFill_Pl1K\"></path><line x1=\"140\" y1=\"336\" x2=\"580\" y2=\"336\" class=\"ground_KAZV\"></line><rect x=\"353\" y=\"200\" width=\"14\" height=\"136\" rx=\"3\" class=\"inkFill_VyiV\"></rect><path d=\"M360,28 L466,212 Q470,222 458,222 L262,222 Q250,222 254,212 Z\" fill=\"#f2b33d\" stroke=\"#16150f\" stroke-width=\"8\" stroke-linejoin=\"round\"></path><g transform=\"translate(312 98) scale(0.95)\" fill=\"#16150f\"><path d=\"M10,62 Q8,54 16,50 L50,30 L58,18 L64,30 Q70,28 78,30 Q74,38 86,44 Q80,52 94,60 Q86,68 98,78 Q88,86 98,98 Q90,104 92,110 L46,110 Q46,92 38,80 Q30,76 20,74 Q12,72 10,62 Z\"></path><path d=\"M41,35.5 L50,30 L32,2 Z\"></path><path d=\"M37,22 L45,18 M40,28 L48,24\" stroke=\"#f2b33d\" stroke-width=\"2\" stroke-linecap=\"round\"></path><circle cx=\"33\" cy=\"47\" r=\"3\" fill=\"#f2b33d\"></circle><circle cx=\"15\" cy=\"63\" r=\"1.6\" fill=\"#f2b33d\"></circle></g><rect x=\"252\" y=\"244\" width=\"216\" height=\"42\" rx=\"8\" class=\"inkFill_VyiV\"></rect><text x=\"360\" y=\"270\" text-anchor=\"middle\" class=\"plate_a4Rv\">BEWARE OF UNICORNS</text></svg></figure>\n<p>Some competitors promise “perfect” solutions no matter what you throw at them, often touting terms like “AI-powered” or “machine learning-enhanced”. We promise “best-effort” solutions, grounded in good old-fashioned algorithmic principles and practical engineering<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#f2\" class=\"\">²</a>.</p>\n<p>Above all, we emphasize that no algorithm can guarantee a flawless match in such a complex, constraint-laden environment. Consider for example the following scenarios, which may look trivial but are inspired by real events:</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"one-for-all-and-all-for-one\">One for all, and all for one<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#one-for-all-and-all-for-one\" class=\"hash-link\" aria-label=\"Direct link to One for all, and all for one\" title=\"Direct link to One for all, and all for one\" translate=\"no\">​</a></h3>\n<p>One organizer wants to set up one-to-one meetings between one single buyer and multiple exhibitors. After receiving our proposal, they complain that exhibitors are mostly idle. The issue here? Unlike <a href=\"https://www.youtube.com/watch?v=2mMUPuDBmhY\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Schrödinger's cat</a>, the buyer is not in a superposition of states and can only be at one place at a time. In other words: exhibitors have to wait their turn.</p>\n<p>Sometimes the organizer's expectations clash with physical and practical constraints, in ways that are not necessarily as obvious as in the above example. HBP keeps track of such conflicts (e.g. people, place, and time availabilities) to help the organizer understand what happened, and why.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"have-your-cake-and-eat-it-too\">Have your cake and eat it too<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#have-your-cake-and-eat-it-too\" class=\"hash-link\" aria-label=\"Direct link to Have your cake and eat it too\" title=\"Direct link to Have your cake and eat it too\" translate=\"no\">​</a></h3>\n<figure class=\"figure_CaPK\"><span class=\"tag_V8fj\">Fig. 3</span><svg viewBox=\"0 0 720 440\" role=\"img\" aria-label=\"Bob and Charlie must meet; Alice and Bob, and Charlie and Diana, could meet; Alice and Diana refuse to meet. One time slot.\"><defs><marker id=\"hbp-q-ink\" viewBox=\"0 0 10 10\" refX=\"8.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"12\" markerHeight=\"12\" orient=\"auto-start-reverse\"><path d=\"M0,0 L10,5 L0,10 z\" class=\"mutedFill_Z4w3\"></path></marker><marker id=\"hbp-q-accent\" viewBox=\"0 0 10 10\" refX=\"8.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"12\" markerHeight=\"12\" orient=\"auto-start-reverse\"><path d=\"M0,0 L10,5 L0,10 z\" class=\"accentFill_Pl1K\"></path></marker><marker id=\"hbp-q-danger\" viewBox=\"0 0 10 10\" refX=\"8.5\" refY=\"5\" markerUnits=\"userSpaceOnUse\" markerWidth=\"12\" markerHeight=\"12\" orient=\"auto-start-reverse\"><path d=\"M0,0 L10,5 L0,10 z\" class=\"dangerFill_MPnb\"></path></marker></defs><path d=\"M236,86 L484,86\" class=\"mustEdge_DIBt\" marker-start=\"url(#hbp-q-accent)\" marker-end=\"url(#hbp-q-accent)\"></path><text x=\"360\" y=\"68\" text-anchor=\"middle\" class=\"mustLabel_yGUG\">MUST MEET!</text><path d=\"M172,156 L172,284\" class=\"maybeEdge_rbCK\" marker-start=\"url(#hbp-q-ink)\" marker-end=\"url(#hbp-q-ink)\"></path><text x=\"190\" y=\"226\" class=\"edgeLabel_bhts\">Why not?</text><path d=\"M548,156 L548,284\" class=\"maybeEdge_rbCK\" marker-start=\"url(#hbp-q-ink)\" marker-end=\"url(#hbp-q-ink)\"></path><text x=\"530\" y=\"226\" text-anchor=\"end\" class=\"edgeLabel_bhts\">Why not?</text><path d=\"M236,354 L484,354\" class=\"noEdge_emCu\" marker-start=\"url(#hbp-q-danger)\" marker-end=\"url(#hbp-q-danger)\"></path><g transform=\"translate(360 354)\"><circle r=\"11\" class=\"noMark_D4qR\"></circle><path d=\"M-4,-4 L4,4 M4,-4 L-4,4\" class=\"noCross_qswY\"></path></g><text x=\"360\" y=\"334\" text-anchor=\"middle\" class=\"noLabel_ByFf\">NO WAY!</text><g transform=\"translate(120 24)\"><rect width=\"104\" height=\"124\" rx=\"12\" class=\"card_MXCK\"></rect><path d=\"M0,12 a12,12 0 0 1 12,-12 h80 a12,12 0 0 1 12,12 v16 h-104 z\" class=\"inkFill_VyiV\"></path><rect x=\"40\" y=\"8\" width=\"24\" height=\"5\" rx=\"2.5\" class=\"paperFill_m4a7\"></rect><circle cx=\"52\" cy=\"60\" r=\"20\" class=\"track1_BAOW\"></circle><text x=\"52\" y=\"67\" text-anchor=\"middle\" class=\"initial_K_IW\">B</text><text x=\"52\" y=\"106\" text-anchor=\"middle\" class=\"name_NGlY\">Bob</text></g><g transform=\"translate(496 24)\"><rect width=\"104\" height=\"124\" rx=\"12\" class=\"card_MXCK\"></rect><path d=\"M0,12 a12,12 0 0 1 12,-12 h80 a12,12 0 0 1 12,12 v16 h-104 z\" class=\"inkFill_VyiV\"></path><rect x=\"40\" y=\"8\" width=\"24\" height=\"5\" rx=\"2.5\" class=\"paperFill_m4a7\"></rect><circle cx=\"52\" cy=\"60\" r=\"20\" class=\"track0_a3nw\"></circle><text x=\"52\" y=\"67\" text-anchor=\"middle\" class=\"initial_K_IW\">C</text><text x=\"52\" y=\"106\" text-anchor=\"middle\" class=\"name_NGlY\">Charlie</text></g><g transform=\"translate(120 292)\"><rect width=\"104\" height=\"124\" rx=\"12\" class=\"card_MXCK\"></rect><path d=\"M0,12 a12,12 0 0 1 12,-12 h80 a12,12 0 0 1 12,12 v16 h-104 z\" class=\"inkFill_VyiV\"></path><rect x=\"40\" y=\"8\" width=\"24\" height=\"5\" rx=\"2.5\" class=\"paperFill_m4a7\"></rect><circle cx=\"52\" cy=\"60\" r=\"20\" class=\"track4_STz0\"></circle><text x=\"52\" y=\"67\" text-anchor=\"middle\" class=\"initial_K_IW\">A</text><text x=\"52\" y=\"106\" text-anchor=\"middle\" class=\"name_NGlY\">Alice</text></g><g transform=\"translate(496 292)\"><rect width=\"104\" height=\"124\" rx=\"12\" class=\"card_MXCK\"></rect><path d=\"M0,12 a12,12 0 0 1 12,-12 h80 a12,12 0 0 1 12,12 v16 h-104 z\" class=\"inkFill_VyiV\"></path><rect x=\"40\" y=\"8\" width=\"24\" height=\"5\" rx=\"2.5\" class=\"paperFill_m4a7\"></rect><circle cx=\"52\" cy=\"60\" r=\"20\" class=\"track2_M6tT\"></circle><text x=\"52\" y=\"67\" text-anchor=\"middle\" class=\"initial_K_IW\">D</text><text x=\"52\" y=\"106\" text-anchor=\"middle\" class=\"name_NGlY\">Diana</text></g></svg></figure>\n<p><em>Quality or quantity?</em></p>\n<p>Consider the small one-time-slot-only HBP described in Figure 3. You have a true decision to make:</p>\n<ul>\n<li class=\"\">Match Alice with Bob, Charlie with Diana, ending up with two acceptable meetings, or</li>\n<li class=\"\">Select the high-profile meeting with Bob and Charlie, leaving Alice and Diana without meetings.</li>\n</ul>\n<p>We cannot stress this enough: no algorithm can magically solve such dilemmas. One must choose between quality (fewer, higher-value meetings) and quantity (more meetings, possibly of lower value). We choose quality, because it is easier to tell Alice and Diana that Bob and Charlie really want to meet than to explain Bob and Charlie that they will not meet because Alice and Diana refuse to meet.</p>\n<p>That being said, quantity has its perks too. Notably it helps fairness, ensuring more participants get at least one meeting. To balance quality and quantity, HBP incorporates a fairness heuristic through its minimum/maximum number of meetings parameters:</p>\n<p><strong>Quality</strong> prevails for a participant until they reach their minimum number of meetings.</p>\n<p><strong>Quantity</strong> takes over afterwards, favoring meetings with under-allocated participants.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"conclusion\">Conclusion<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>Swapcard's <a href=\"https://www.swapcard.com/blog/hosted-buyer-program-guide\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Hosted Buyer Program</a> is a smart solution to the complex challenge of matching buyers and exhibitors. Built on top of academic research, it uses a scoring system that balances organizer goals with participant preferences, ensuring fair, flexible, and high-quality meetings.</p>\n<p>For users, this means faster, more relevant matches and a smoother experience overall. HBP processes thousands of participants and meeting options in seconds, turning a complicated process into an efficient, personalized journey that helps everyone get more value from the event.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"references\">References<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#references\" class=\"hash-link\" aria-label=\"Direct link to References\" title=\"Direct link to References\" translate=\"no\">​</a></h3>\n<p><a id=\"ref1\"></a>[1] D. Gale and L. S. Shapley, “College Admissions and the Stability of Marriage,” <em>The American Mathematical Monthly</em>, vol. 69, no. 1, pp. 9–15, 1962, Accessed: Sep. 05, 2025. [Online]. Available: <a href=\"http://www.jstor.org/stable/2312726\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">http://www.jstor.org/stable/2312726</a></p>\n<p><a id=\"ref2\"></a>[2] F. Mathieu, “Self-stabilization in preference-based systems,” <em>Peer-to-Peer Networking and Applications</em>, vol. 1, no. 2, pp. 104–121, 2008, doi: <a href=\"https://doi.org/10.1007/s12083-008-0009-3\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">10.1007/s12083-008-0009-3</a>.</p>\n<p><a id=\"ref3\"></a>[3] F. Mathieu, G. Postelnicu, and J. Reynier, “The Stable Configuration of Acyclic Preference-Based Systems,” in <em>INFOCOM 2009</em>, Rio de Janeiro, Brazil: IEEE, Apr. 2009, pp. 1440–1448. doi: <a href=\"https://doi.org/10.1109/INFCOM.2009.5062060\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">10.1109/INFCOM.2009.5062060</a>.</p>\n<p><a id=\"ref4\"></a>[4] C. Comte, F. Mathieu, S. M. Varma, and A. Bušić, “Online Stochastic Matching: A Polytope Perspective,” Nov. 2024. [Online]. Available:<a href=\"https://hal.science/hal-03502084\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://hal.science/hal-03502084</a></p>\n<p><a id=\"ref5\"></a>[5] C.-P. Teo, J. Sethuraman, and W.-P. Tan, “Gale-Shapley Stable Marriage Problem Revisited: Strategic Issues and Applications,” <em>Management Science</em>, vol. 47, no. 9, pp. 1252–1267, 2001, doi:<a href=\"https://doi.org/10.1287/mnsc.47.9.1252.9784\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">10.1287/mnsc.47.9.1252.9784</a>.</p>\n<hr>\n<p><a id=\"f1\"></a>¹Incidentally, my own research touches on this field (<a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#ref2\" class=\"\">[2]</a>, <a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#ref3\" class=\"\">[3]</a>, or <a href=\"https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/#ref4\" class=\"\">[4]</a>).</p>\n<p><a id=\"f2\"></a>²And yes, before you ask: it's also “AI-powered” and “machine learning-enhanced”. But what isn't nowadays?</p>",
            "url": "https://swapcard.engineering/hosted-buyer-program-looking-under-the-hood/",
            "title": "Hosted Buyer Program: Looking Under the Hood",
            "summary": "The algorithms and engineering behind Hosted Buyer Programs: how we match prequalified buyers with the right exhibitors at B2B events.",
            "date_modified": "2025-11-04T17:23:29.000Z",
            "author": {
                "name": "Fabien Mathieu"
            },
            "tags": [
                "Machine learning"
            ]
        },
        {
            "id": "https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/",
            "content_html": "<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"introduction\">Introduction<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#introduction\" class=\"hash-link\" aria-label=\"Direct link to Introduction\" title=\"Direct link to Introduction\" translate=\"no\">​</a></h2>\n<p>Today, more and more companies are adopting a fully remote policy. Along with the advantages come a few new challenges. Among them the one we’re going to focus in this article: how to organize a team without ever meeting them.</p>\n<p>Swapcard was founded in Paris and started small with the entire team being in Paris at the time. Now, the company is more than 200 people strong, but only around 30 people still work from the Paris office. The rest of the people are scattered throughout the world. Swapcard’s culture is 100% directed at how best to integrate each and every one of its employees and they’re doing a terrific job, but it’s still up to each department to organize collaboration in their ranks.</p>\n<p>The frontend team (which I’m a happy member of) has been experimenting with various methods and processes, and this article aims at detailing how those tools are making our everyday collaboration and growth a success. I will specifically be talking about where the information exists and how it’s organized; how rules and practices are discussed and evolve; and how we enforce their usage.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"tools-in-use\">Tools in use<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#tools-in-use\" class=\"hash-link\" aria-label=\"Direct link to Tools in use\" title=\"Direct link to Tools in use\" translate=\"no\">​</a></h2>\n<p>To achieve the processes we have, we’re using tools most companies are familiar with, I’m listing here those that will be mentioned further into the article:</p>\n<p><strong>Notion:</strong> It’s the knowledge base of the company. We use it for all kinds of company-wide information and rules such as internal rules, org charts, processes, and contacts. We also use it for local things like the Paris office’s wifi and trash sorting rules. Each department uses it for their own internal documentation, write down all of their processes, rules, meetings reports, etc. Basically, everything that anybody could have a question about at any point.</p>\n<p><strong>Slack:</strong> It’s used company-wide for both important company communication and everyday professional/personal chats. We created several channels each with a very specific purposes so no channel is cluttered and information is easily findable.</p>\n<p><strong>Jira:</strong> It’s used by all the engineering department for project management. We have boards for every team but also shared ones that I will talk more about further down.</p>\n<p><strong>Agile methodology:</strong> Our project management framework doesn’t follow the SCRUM guidelines to the letter but is still very close to it. This means we have all the scrum ceremonies like daily stand-ups, sprint plannings, retrospective meetings, and others.</p>\n<p><strong>Team organization:</strong> It is based on the <a href=\"https://medium.com/@achardypm/agile-team-organisation-squads-chapters-tribes-and-guilds-80932ace0fdc\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Spotify model</a> and aims to scale the agile approach and keep an innovative, creative startup vibe. The product and the tech team are organized in:</p>\n<ul>\n<li class=\"\"><strong>Squads</strong> → team that is capable to deliver full features by itself.</li>\n<li class=\"\"><strong>Chapters</strong> → is a cross-squad, cross-tribe group of developers for the same technology.</li>\n<li class=\"\"><strong>Tribes</strong> → it's a group of squads that is centered around the same goal.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"centralized-information\">Centralized information<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#centralized-information\" class=\"hash-link\" aria-label=\"Direct link to Centralized information\" title=\"Direct link to Centralized information\" translate=\"no\">​</a></h2>\n<p>When sharing physical offices with your team you get organically onboarded by the people, day by day. Every process gets explained, observed, repeated and assisted by someone until they’re assimilated. If something changes, the transition happens the same way since everybody around us transitions to it in the same physical space.</p>\n<p>When working remotely there is no such natural flow of information happening; so all the information needs to be written down in a centralized place and thoroughly detailed, a lot like documentation.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"general-documentation\">General documentation<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#general-documentation\" class=\"hash-link\" aria-label=\"Direct link to General documentation\" title=\"Direct link to General documentation\" translate=\"no\">​</a></h3>\n<p>Before our developers can even contribute to the codebase, we need them to be able to run our various apps locally. So, we created pages to detail how they should be installed, how the configuration files should be added and what they should contain. This may seem trivial but as our tools and libraries evolve, someone may add an item to an apps configuration – which would make running it fail for everybody without an up-to-date config file. So all additions are added to the documentation, and the change is notified in the appropriate Slack channel so everyone can update their setup.</p>\n<p>In our team anybody can do releases, be it in the dev, staging or prod environment. So we need to document each of those processes for each repository detailing each step to make sure no one breaks anything. Those steps also include warning about releases in Slack and giving timely status updates. This ensures everyone is aware of what’s happening at each moment so they can decide if they may proceed with their tasks or not.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"coding-patterns\">Coding patterns<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#coding-patterns\" class=\"hash-link\" aria-label=\"Direct link to Coding patterns\" title=\"Direct link to Coding patterns\" translate=\"no\">​</a></h3>\n<p>To help keep a good consistency in our codebases, we also have preferred ways of writing different kinds of code blocks and patterns to implement. For instance we have guidelines about how to handle fetching, rendering loading states, rendering errors, etc. We also have guidelines about how we should generate mock data, write stories (we use Storybook extensively), and how they should be reused in our tests. Those come with numerous code examples, edge cases covering, warnings and explanations about why we’re preferring what pattern.</p>\n<p>Those pages, obviously, are always kept up to date as our guidelines change.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"dos-and-donts\">Do’s and don’ts<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#dos-and-donts\" class=\"hash-link\" aria-label=\"Direct link to Do’s and don’ts\" title=\"Direct link to Do’s and don’ts\" translate=\"no\">​</a></h3>\n<p>Finally, for all guidelines that do not fit in a broader category we have the do’s and don’ts section. There we list all preferences regarding general code styling and good practices such as syntax specifics and naming.</p>\n<p>The do’s and don’ts section is our reference in case of doubts or \"conflicts\" during discussions or PR review, and the items in it are translated into linter rules when applicable (see “enforcing the rules” section).</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://swapcard.engineering/assets/images/image-1-eff701f694f3485369c1497f74aad0e9.png\" width=\"1448\" height=\"1162\" class=\"img_SS3x\"></p>\n<p>All those guidelines ensure code written by developers from different squads will be as homogeneous as possible. It has numerous advantages: it eases PR reviews, makes getting acquainted with unfamiliar parts of the codebase way easier, and it makes sure everybody uses code that performs better and is less bug-prone.</p>\n<p>Now that all this information exists we need to make it known by developers.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"onboarding-process\">Onboarding process<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#onboarding-process\" class=\"hash-link\" aria-label=\"Direct link to Onboarding process\" title=\"Direct link to Onboarding process\" translate=\"no\">​</a></h3>\n<p>The process of integrating a new person into the company and bringing them up to speed with the current state of the codebase and the practices is a great occasion to map all the knowledge we created earlier. Since the company grew very quickly over the course of 2020 and 2021 (+150 employees) we put in place a thorough onboarding guide for greeting each new member in the team. The guide walks them through all our documentation so they learn about our ways and know where to find information they need.</p>\n<p>This guide can also serve as a reference for existing employees to find the content they’re looking for.</p>\n<p><strong>Kanban board</strong></p>\n<p>Most developer teams already use ticket-based frameworks for tracking their tasks’ statuses so we figured setting up a kanban board for tracking specific parts of the onboarding process would feel natural to most recruits. Right now we’re using it only for the environment setup, but it could be done for the rest of the onboarding too. The blocks redirect to the aforementioned pages detailing all the steps to complete – so that anybody onboarding or starting from a fresh setup can be ready in less than a day.</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://swapcard.engineering/assets/images/image-2-63ab4da531b840468ec9652ab9e9e4bb.png\" width=\"1718\" height=\"578\" class=\"img_SS3x\"></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"discussion-growth-and-evolution\">Discussion, growth and evolution<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#discussion-growth-and-evolution\" class=\"hash-link\" aria-label=\"Direct link to Discussion, growth and evolution\" title=\"Direct link to Discussion, growth and evolution\" translate=\"no\">​</a></h2>\n<p>At Swapcard, our developer base is divided into squads. Each squad works on topics or apps whose specifics are not discussed with other squads on a daily basis. If each squad handled their subjects independently without ever discussing them with the rest of the team, our practices and procedures would grow apart. This could result in a codebase that is complicated to maintain and conflicting approaches to PR reviews. This is why we created a weekly meeting for the entire team, called the chapter meeting.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"chapter-meetings\">Chapter meetings<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#chapter-meetings\" class=\"hash-link\" aria-label=\"Direct link to Chapter meetings\" title=\"Direct link to Chapter meetings\" translate=\"no\">​</a></h3>\n<p>I would say that those meetings are the backbone of our team’s communication and growth. They happen every week on the same day and at the same time and usually last between 45 and 90 minutes.</p>\n<p>We have a dedicated section on Notion for them, in which can be found the reports of all previous meetings, the topics discussed, the conclusions and the actions that were taken. Before each meeting a page is available in this section called “Next chapter meeting”. There, any developer can go and create a topic using the template we put in place and describe the topic in as much details as needed for everyone to understand it.</p>\n<p>All kinds of subjects can be invoked during those meetings. Examples include:</p>\n<ul>\n<li class=\"\">“I had this problem and solved it this way, what do you think?”</li>\n<li class=\"\">“I've been seeing different ways to do the same thing around the codebase, what do you think would be the best approach?”</li>\n<li class=\"\">“We're doing this but we should stop because X and Y”</li>\n<li class=\"\">“I've seen this new lib and think we should use it, what are your thoughts?”</li>\n<li class=\"\">“I saw this other pattern somewhere, what about implementing it in our codebase?”</li>\n<li class=\"\">DX improvement ideas (scripts, processes, tools, etc.)</li>\n<li class=\"\">Ideas for new projects inside the company</li>\n</ul>\n<p>Topics are debated at length and in depth with references to benchmarks, studies and articles when possible to document each opinion and take the best decision for the team. We often learn something in those and end the meeting with interesting reading lists.</p>\n<p>Most of the time, those discussions result in new or updated guidelines, processes or projects. If we decide to bump some packages (like Storybook, React or Next for instance) it unlocks new patterns for us to discuss. So if we bump Storybook, we’re going to create one or more do’s and don’ts items, probably refresh the documentation page about how we write code/tests/stories and finally create tickets in our shared Jira board to refactor the existing code so we always keep our codebase aligned with our rules.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"shared-board\">Shared board<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#shared-board\" class=\"hash-link\" aria-label=\"Direct link to Shared board\" title=\"Direct link to Shared board\" translate=\"no\">​</a></h3>\n<p>The shared board is a Jira board shared across the entire chapter, unlike the squad-specific ones that are private to each squad. All tickets discussed during the chapter meeting would be created there, then discussed again if needed to agree on specs. Once a ticket is ready to be assigned, people can either freely take it or request during quarter meetings to work on it. At the beginning of each quarter we usually go over the “ready tickets” backlog and try to assign them to people. This allows everyone to know which chapter subject to focus on during the months to come. As with a regular agile board, progress is tracked on the tickets and they eventually finish in the “Done” column.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"10-of-sprint-velocity-for-chapter-tickets\">10% of sprint velocity for chapter tickets<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#10-of-sprint-velocity-for-chapter-tickets\" class=\"hash-link\" aria-label=\"Direct link to 10% of sprint velocity for chapter tickets\" title=\"Direct link to 10% of sprint velocity for chapter tickets\" translate=\"no\">​</a></h3>\n<p>Of course, since the shared board tickets are not directly linked to a squad, they’re not accounted for in the product roadmap and are not naturally part of future sprint backlogs.</p>\n<p>To ensure developers can continuously work on chapter tickets, the company has agreed to allocate around 15% of each sprint’s velocity to them. Because really, reducing the tech debt improves the overall developer experience, speeding up development cycles and helping reducing the introduction of new bugs.</p>\n<p>Swapcard’s top management very well understood the mid and long-term benefits of continuously fighting tech debt, and strongly encourages us to take advantage of those 10%.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"enforcing-the-rules\">Enforcing the rules<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#enforcing-the-rules\" class=\"hash-link\" aria-label=\"Direct link to Enforcing the rules\" title=\"Direct link to Enforcing the rules\" translate=\"no\">​</a></h2>\n<p>Now that we’ve made sure the rules are well-structured and accessible – and that we have a process to challenge them and make them evolve – we need to make sure people actually abide by them.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"centralized-lint-config\">Centralized lint config<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#centralized-lint-config\" class=\"hash-link\" aria-label=\"Direct link to Centralized lint config\" title=\"Direct link to Centralized lint config\" translate=\"no\">​</a></h3>\n<p>Right now we have apps in different repositories (although we’re currently <a href=\"https://vercel.com/blog/monorepos\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">migrating them into a monorepo</a>), and we also have numerous separate internal packages we’re using in our different apps. In order to maintain consistency and to enforce our code style rules we created a shared lint config used everywhere in our codebase. This ensures all code from everywhere remains up to date with our evolving standards.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"mandatory-and-scrupulous-pr-reviews\">Mandatory and scrupulous PR reviews<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#mandatory-and-scrupulous-pr-reviews\" class=\"hash-link\" aria-label=\"Direct link to Mandatory and scrupulous PR reviews\" title=\"Direct link to Mandatory and scrupulous PR reviews\" translate=\"no\">​</a></h3>\n<blockquote>\n<p>Try to enforce that at least once a day all of your pending PR reviews are pruned.</p>\n</blockquote>\n<p>PRs are very important and have at least two purposes:</p>\n<ul>\n<li class=\"\">The first one is obviously reviewing the code that goes into production to make sure no bugs or unwanted side-effects are introduced. This is also the opportunity to make sure the new code follows all our guidelines!💡</li>\n<li class=\"\">The second one is more at the advantage of the reviewer. It allows them to remain up to date with what happens in the codebase, witness what solutions people come up with to problems they may encounter themselves, and potentially spark conversations that might result in chapter topics.</li>\n</ul>\n<p>We’ve also taken advantage of Github’s possibility to create PR templates to enforce some of the good practices we’ve agreed upon. Those templates include various things such as our PR title being properly formatted; the PR having added or updated stories and tests to account for the code changes; and making sure the changes meet our accessibility standards. Those templates are an extension of our rules. They also make sure our guidelines are remembered and applied.</p>\n<p>Additionally, the PR template makes sure the PR creator has made their changes available on the dev environment, to ensure both QA and reviewers can test the feature. For reviewers, mandatory testing on the dev environment is highly beneficial: it helps better prevent regressions or bugs, but it also drastically increases each contributor’s product knowledge and meta skills.</p>\n<p>In order for a PR to be ready to merge, it requires the approval of at least two reviewers and must have a pass on all its pipelines. The pipelines basically test if the code is buildable – there are no typing issues, unit tests pass and e2e tests pass.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"usage-of-slack-channels\">Usage of Slack channels<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#usage-of-slack-channels\" class=\"hash-link\" aria-label=\"Direct link to Usage of Slack channels\" title=\"Direct link to Usage of Slack channels\" translate=\"no\">​</a></h3>\n<p>Last but not least, we’re using Slack channels to separate various communication contexts. We have an all-purpose channel, but also a few others dedicated to very specific usage:</p>\n<ul>\n<li class=\"\"><strong>Private:</strong> for everyday discussions, but also general ones about our apps, weird errors reports, questions, etc.</li>\n<li class=\"\"><strong>Releases:</strong> whenever someone starts a release process, to inform about the start, progression, possible issues and finish of a release, in real time (with updating emoji for statuses ⏳/⚙️/✅). This is a direct application of how our release processes are structured and described in Notion.</li>\n<li class=\"\"><strong>War-room:</strong> created as needed when an urgent bug is reported, with only the needed members and deleted when the bug is solved. Our SLA demands very quick resolution times of us and those rooms should always be treated with top priority.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"conclusion\">Conclusion<a href=\"https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>Personally, the biggest advantages of working in a company instead of freelancing are the people, the discussions, and the ability to grow when working with talented people. But, in a fully remote company you might not really benefit from those advantages if the communication and organization are not handled properly. Even worse, you might end-up feeling a bit lonely and isolated.</p>\n<p>Our processes are obviously not perfect and we’re continuously working on improving them, but I can say that so far they’ve been bringing to the table everything I expect to find in terms of growth, thinking and discussions when working with people.</p>",
            "url": "https://swapcard.engineering/how-to-organize-and-grow-as-a-full-remote-engineering-team/",
            "title": "Navigating the Challenges of Organizing a Fully Remote Engineering Team",
            "summary": "As more companies go fully remote, organizing a team without ever meeting them presents a challenge. Strategies like effective communication, project management tools, and team-building activities can help create a productive remote team.",
            "date_modified": "2023-04-20T17:23:12.000Z",
            "author": {
                "name": "Ronan Guiho",
                "url": "https://www.linkedin.com/in/rguiho/"
            },
            "tags": [
                "Culture"
            ]
        },
        {
            "id": "https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/",
            "content_html": "<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"the-idea\">The idea<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#the-idea\" class=\"hash-link\" aria-label=\"Direct link to The idea\" title=\"Direct link to The idea\" translate=\"no\">​</a></h2>\n<p>In this article, we will see how to create a self-adjusting video call app layout with HTML, CSS, and JavaScript in 5 minutes! 🚀</p>\n<!-- -->\n<p>The objective is to scale the layout blocks proportionally to the screen depending on the number of participants on the video call.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"the-minimal-setup\">The minimal setup<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#the-minimal-setup\" class=\"hash-link\" aria-label=\"Direct link to The minimal setup\" title=\"Direct link to The minimal setup\" translate=\"no\">​</a></h2>\n<p>You can either walk through the minimal setup steps below or access the codepen with the premade setup at the end of this section.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"html\">HTML<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#html\" class=\"hash-link\" aria-label=\"Direct link to HTML\" title=\"Direct link to HTML\" translate=\"no\">​</a></h3>\n<p>Create a single wrapper element and as many child tile elements as you may need. Please note that the <strong>id</strong> and <strong>class</strong> names need to match the JavaScript that will be shown below.</p>\n<p>You can add as many <strong>.video-tile</strong> elements as you wish.</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://swapcard.engineering/assets/images/image-1-2d4090e6be1c70fa1b27f6e3e1cfe854.png\" width=\"379\" height=\"239\" class=\"img_SS3x\"></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"css\">CSS<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#css\" class=\"hash-link\" aria-label=\"Direct link to CSS\" title=\"Direct link to CSS\" translate=\"no\">​</a></h3>\n<p>The <strong>flex-wrap</strong> is the key for this layout to work, also instead of <strong>aspect-ratio,</strong> you can set a fixed size for the elements if you wish.</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://swapcard.engineering/assets/images/image-2-c4fcdb22be10f797e17442596104d3e5.png\" width=\"302\" height=\"365\" class=\"img_SS3x\"></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"javascript\">JavaScript<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#javascript\" class=\"hash-link\" aria-label=\"Direct link to JavaScript\" title=\"Direct link to JavaScript\" translate=\"no\">​</a></h3>\n<p>Using a combination of <strong>Math.ceil</strong> and <strong>Math.sqrt</strong> we can predict the desired number of elements in a row on the self-adjusting canvas. Thanks to this information, we can compute the value for each element with respect to how many elements we want to see in a single row (and subtract the gap between elements).</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://swapcard.engineering/assets/images/image-3-722e5f8b5d27427e977db592e2dbb265.png\" width=\"676\" height=\"436\" class=\"img_SS3x\"></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"result\">Result<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#result\" class=\"hash-link\" aria-label=\"Direct link to Result\" title=\"Direct link to Result\" translate=\"no\">​</a></h3>\n<p>That's it for the minimal setup, you should have a ready self-adjusting layout with the number of tiles that you specified in the HTML section.</p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAo8AAAEWCAYAAAAHEX1tAAAACXBIWXMAAAsTAAALEwEAmpwYAAAUaElEQVR4nO3dQYogyRFE0YK4/zF0x9mN1hJCZEszbe7m74PtelGYZUR8ioL++RMAAAD4yM/XfwgAAACQRwAAAHyGPAIAAOAz5BEAAACfIY8AAAD4DHkEAADAZ8gjAAAAPkMeAQAA8BnyCAAAgM+QRwAAAHyGPAIAAOAz5BEAAACfIY8AAAD4DHkEAADAZ8gjAAAAfk0ef35+RAe+gb/4G/iPB853pgPfwF/+DThrzpVz9fNbOiCPDpvD9psOGXn0rblvnDXfgHvgp6AD8jhgBOnugDzmN5AbHThr+Q3k50QH5HHACNLdgQctv4Hc6MBZy28gPyc6+K/y+Mc/nujAN/CL38D/8qD5znxnvoFf/wacNefGuXm/pQPy6LA5bKFDRh59e+4fZ8034B74Y2EH5HHACNLdAXnMbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9EBeRwwgnR34EHLbyA3OnDW8hvIO9HBL8mj6MA38P9/A/8JvTpbvoG//htw1pwr5+rnt3RAHh02h+03HTLy6Ftz3zhrvgH3wE9BB+RxwAjS3QF5zG8gNzpw1vIbyM+JDsjjgBGkuwMPWn4DudGBs5bfQH7uySMAAADwBfIIAACAz5BHAAAAfIY8AgAA4DPkcTHpP5wVHTR+A85ZfgPp7wC7seBi0odfdND4DThn+Q2kvwPsxoKLSR9+0UHjN+Cc5TeQ/g6wGwsuJn34RQeN34Bzlt9A+jvAbiy4mH8/jOn/QF10sPEb+FV5TP+8ooON3wB57II8Lsajlr8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuBjymL8QZX8H5DG/gfR3QB67II+LIY/5C1H2d0Ae8xtIfwfksQvyuJh/P4yiA9/A//8NOGfOkXP0938D2I0FF+OC88j5Bv7+R03HzplvgDziXyGPi3GhedR8A+TRN+Ae2PgNYDcWXEz68IsOGr8B5yy/gfR3gN1YEAAAAJ8hjwAAAPgMeQQAAMBnyCMAAAA+Qx4BAADwGfIIAACAz5BHAAAAfIY8AgAA4DPkEQAAAJ8hjwAAAPgMeQQAAMBnyCMAAAA+Qx4BAABAHgEAAPDX4zePAAAA+Ax5BAAAwJ9f+Sf8xwSlAiEOeAAAAABJRU5ErkJggg==\" width=\"655\" height=\"278\" class=\"img_SS3x\"></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"codepen-with-premade-setup\">Codepen with premade setup<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#codepen-with-premade-setup\" class=\"hash-link\" aria-label=\"Direct link to Codepen with premade setup\" title=\"Direct link to Codepen with premade setup\" translate=\"no\">​</a></h3>\n<iframe src=\"https://codepen.io/mecies/embed/preview/vYrvZGq?default-tabs=js%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=vYrvZGq\" title=\"Video app layout\" height=\"300\" loading=\"lazy\" allowfullscreen=\"\" style=\"width:100%;border:0;border-radius:12px\"></iframe>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"the-dynamic-setup\">The dynamic setup<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#the-dynamic-setup\" class=\"hash-link\" aria-label=\"Direct link to The dynamic setup\" title=\"Direct link to The dynamic setup\" translate=\"no\">​</a></h2>\n<p>I've also prepared a more advanced sandbox where you can play with a different number of tiles through input dynamically and see how the self-adjusting layout behaves in action. 😎</p>\n<iframe src=\"https://codepen.io/mecies/embed/preview/BaVvZMR?default-tabs=js%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=BaVvZMR\" title=\"Video app layout dynamic\" height=\"300\" loading=\"lazy\" allowfullscreen=\"\" style=\"width:100%;border:0;border-radius:12px\"></iframe>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"congrats\">Congrats<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#congrats\" class=\"hash-link\" aria-label=\"Direct link to Congrats\" title=\"Direct link to Congrats\" translate=\"no\">​</a></h2>\n<p>That's it!<br>\n<!-- -->You just created a self-adjusting video-app-like layout that allows you to add as many elements as you want without having to redo a specific CSS.</p>\n<p>I hope you found it useful and easy to implement. 🤝</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"bonus\">Bonus<a href=\"https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/#bonus\" class=\"hash-link\" aria-label=\"Direct link to Bonus\" title=\"Direct link to Bonus\" translate=\"no\">​</a></h2>\n<p>We use this exact layout in production with our live video backstage app!</p>\n<p>To find out more about the video backstage solution at Swapcard visit <a href=\"https://release.swapcard.com/introducing-speaker-backstage-3yviJa\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">https://release.swapcard.com/introducing-speaker-backstage-3yviJa</a></p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https://swapcard.engineering/assets/images/image-5-eb4b91bb81654d756da11cd695faf22d.png\" width=\"1186\" height=\"842\" class=\"img_SS3x\"></p>",
            "url": "https://swapcard.engineering/create-self-adjusting-layout-for-your-video-app-with-html-css-javascript/",
            "title": "Designing for Dynamic Display: How to Create a Self-Adjusting Layout",
            "summary": "Learn how to create a dynamic layout for a video call app, that adjusts proportionally to the number of participants on the call, making the best use of the available screen space.",
            "date_modified": "2023-01-26T08:52:35.000Z",
            "author": {
                "name": "Maciej Hnat"
            },
            "tags": [
                "Frontend"
            ]
        },
        {
            "id": "https://swapcard.engineering/what-i-learned-over-the-years/",
            "content_html": "<p>There are a few things that I've learned at Swapcard over the years, sometimes the hard way, sometimes by using common sense. Always by being human.</p>\n<p>I can sum it up with one simple sentence: \"it's all about balance ⚖️.\" But that's more difficult than you might think.</p>\n<blockquote>\n<p>To get the most from your engineering team, you need to make them feel connected to the company. Don't operate as a black box; explain the company's issues, share the challenges, and always be transparent.</p>\n</blockquote>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"career-path\">Career path<a href=\"https://swapcard.engineering/what-i-learned-over-the-years/#career-path\" class=\"hash-link\" aria-label=\"Direct link to Career path\" title=\"Direct link to Career path\" translate=\"no\">​</a></h2>\n<p><strong>Create a career path</strong>. If you want to prevent turnover and motivate your people, you need to embrace the career path. They need to know how you are going to make the most of their potential. Whether it's an individual contributor or high-level management, they need to understand how they can grow within the company.</p>\n<p><strong>Make sure your engineering team knows that they can grow within the company.</strong> You need to find the right balance between hiring externally and promoting internally, <strong>which can be tricky</strong>. Promoting internally will help you create a core team that grows with the company. If you give your people a chance and show that you trust them, your people will follow you almost anywhere.</p>\n<p><strong>Becoming a manager is not the only way to grow as an engineer.</strong> Don't force people to become managers when that isn't the best use of their skillset. It's better to have a great individual contributor rather than a manager that doesn't like what they are doing. You want to set your people up for success, which means identifying what they do best and putting them in positions to succeed.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"behaviour\">Behaviour<a href=\"https://swapcard.engineering/what-i-learned-over-the-years/#behaviour\" class=\"hash-link\" aria-label=\"Direct link to Behaviour\" title=\"Direct link to Behaviour\" translate=\"no\">​</a></h2>\n<p><strong>Be human.</strong> You can use a lot of methodologies in your work, but nothing replaces the human and social elements. You can solve the majority of issues by showing a willingness to listen to people and genuinely take what they say to heart. <strong>Think about the yellow rubber duck</strong> - externalizing the issue you are facing and talking it out helps to find the problem and address it. Don't sweep your problems under the carpet where they'll collect dust. Tackle them head-on.</p>\n<p>We all make decisions based on the information we have available to us at a specific moment. It's important to own up to your past decisions and be humble when you've made a mistake. <strong>You can be wrong; mistakes are how we learn and grow.</strong> It takes a strong leader to be able to recognize where they went wrong, iterate on it, and fix it.</p>\n<p>As a leader, you need to be a problem solver and believe in the company vision. <strong>You need to earn respect and legitimacy, you cannot just ask for it. You will earn it by your day-to-day actions, which you are responsible for.</strong></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"management\">Management<a href=\"https://swapcard.engineering/what-i-learned-over-the-years/#management\" class=\"hash-link\" aria-label=\"Direct link to Management\" title=\"Direct link to Management\" translate=\"no\">​</a></h2>\n<p><strong>Engineers need to use and understand the product they're working on.</strong> This seems pretty basic, but as pure engineers, we sometimes tend to forget this part. <strong>Remind yourself that you aren't just developing some engineering solution; you're developing a product. You're the backbone that solves issues or creates new needs for people and companies.</strong></p>\n<p><strong>Don't ask people to do things that you don't even understand or have any idea about</strong>. Of course, you cannot know everything. But have a willingness to learn and to have an opinion about a product or technology, especially if you need to manage it. This goes back to my earlier point; be curious and humble and ask people to explain to you how things work. <strong>Always be willing to learn and respect the knowledge your people bring to your team.</strong></p>\n<p>Speaking of team, <strong>don't hesitate to talk with other teams and divisions</strong>. It's easy to be protective of your people and work, but it doesn't help your engineering team if they feel separate from the rest of the company. <strong>By working together with other teams, you can solve the most significant issues and even identify problems before they happen.</strong> You will for sure discover many quick wins that you can implement to help the company operate at scale and make everyone's life easier.</p>\n<p>When it comes to running a team, accept that things can be chaotic. <strong>Chaos management tends to help provide structure, then iterate and find the right balance between agility and processes.</strong></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"engineering\">Engineering<a href=\"https://swapcard.engineering/what-i-learned-over-the-years/#engineering\" class=\"hash-link\" aria-label=\"Direct link to Engineering\" title=\"Direct link to Engineering\" translate=\"no\">​</a></h2>\n<p><strong>The perfect engineering solution doesn't exist; it's all about the pros &amp; cons. Don't over-engineer and try to solve issues that don't exist for today's business or product.</strong> It's important to anticipate the future while also balancing current needs in order to develop something that can scale. It's not about being perfect. It's all about finding the right balance. This takes practice, but it's an important point to remember.</p>\n<p>Be careful about taking on the famous quest of developing \"agnostic\" solutions. That's often pretty hard and you cannot develop something agnostic when you only implement one provider at a time. You should be able to easily switch from one provider to another, but this doesn't mean you need to develop an entire system for something that may happen in a few years.</p>\n<p>There is no bad technology (well, maybe sometimes), but ultimately it's always about solving problems. I remember how bad PHP's reputation was during my time at engineering school, even though it's actually used by a lot of successful companies and has run at scale for most of them.</p>\n<p><strong>Make sure the QA team is part of engineering and don't make them operate as an isolated entity.</strong> Continuous delivery is key, and for this, you need to accept that zero risk is a dream (it depends on the product you are working on, of course). However, keep in mind that there are plenty of technologies that can help you remain agile and limit risk (i.e. feature flags, traffic deployment, error reporting, APM, centralized logging, E2E testing, QA review, etc.)</p>\n<p><strong>Sometimes you need to break things to make them better.</strong> If a policy slows you down, remove it and try to operate without it. You cannot cover 100% of the daily actions by policy and procedures. You might be able to do this if you're Google, but not when you scale and run a new business.</p>\n<p><strong>Be careful when it comes to trends regarding technology.</strong> You can read articles about how X technology is dying, and 6 months later, read another article about how this technology is spreading all over the engineering world. Stay up-to-date with the technologies that are out there, but be wary when it comes to what's trending.</p>\n<p><strong>Choose the right infrastructure tools and adapt them to your needs.</strong> Don't run Terraform and Kubernetes if you run a small business with 2 nodes. Instead, use the AWS Console, make an easy deployment pipeline with Github Actions or Gitlab, and then ECS, for example. You might not even need to use a huge cloud provider right away. You can use a dedicated server and run deployment over SSH if the business is just starting. <strong>Understand what your system needs and choose the tools best suited for it.</strong></p>\n<p><strong>Embrace open-source services.</strong> You can start by doing self-hosting if you need to keep costs low. Once you can afford managed services, do it.</p>\n<p><strong>Don't copy &amp; paste organization models from famous companies because they're successful.</strong> Take inspiration from it and adapt it to your needs, culture, organization, and business. Just because it works at another company doesn't mean that it's going to work for you. All organizations are different, so be sure to embrace what makes you stand apart and put that to work for you.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_tleR\" id=\"others\">Others<a href=\"https://swapcard.engineering/what-i-learned-over-the-years/#others\" class=\"hash-link\" aria-label=\"Direct link to Others\" title=\"Direct link to Others\" translate=\"no\">​</a></h2>\n<p><strong>SLA is all about trust. Don't hide issues.</strong> Customers tend to be more understanding when you inform them about an issue right away and then follow up with a detailed post-mortem. Open a public status page that you share with your clients so that they can have easy access to this information.</p>\n<p><strong>Security is incredibly important and should be a central consideration from day one</strong>. If you want to build a sustainable product and prevent security issues from constantly arising, you need to start training people about security and use the right tools from the beginning.</p>\n<p><strong>Run bug bounty programs, there are usually offerings geared towards startups and it can help you gain confidence in your security.</strong> You will also learn from the issues reported and can create better guidelines for your engineers.</p>\n<p>One day, you will need IT support internally, especially if you are a distributed team. Make sure that you keep this in mind because <strong>the need for IT support will arise faster than you might think.</strong> They provide help with ordering assets at scale, handling tool access, tool reviews, common IT issues, and more. Be sure to plan for this, you'll be glad that you did.</p>",
            "url": "https://swapcard.engineering/what-i-learned-over-the-years/",
            "title": "What I've Learned: Reflections on Life, Work, and Growth",
            "summary": "Maximize your engineering team's potential by fostering a sense of connection to the company, through transparency and communication of challenges and issues.",
            "date_modified": "2021-11-10T08:51:00.000Z",
            "author": {
                "name": "Ronan Guiho",
                "url": "https://www.linkedin.com/in/rguiho/"
            },
            "tags": [
                "Culture"
            ]
        }
    ]
}