<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Deezer I/O - Medium]]></title>
        <description><![CDATA[Deezer’s official engineering blog featuring engineering, product, design, data and research stories. - Medium]]></description>
        <link>https://medium.com/deezer-engineering?source=rss----2ab7e077175a---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Deezer I/O - Medium</title>
            <link>https://medium.com/deezer-engineering?source=rss----2ab7e077175a---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 02:00:17 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/deezer-engineering" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[How we slashed the size of our search cache by 74%]]></title>
            <link>https://medium.com/deezer-engineering/how-we-slashed-the-size-of-our-search-cache-by-74-4e2bd5fd65e0?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/4e2bd5fd65e0</guid>
            <category><![CDATA[memcached]]></category>
            <category><![CDATA[jvm]]></category>
            <category><![CDATA[data-structures]]></category>
            <category><![CDATA[performance-engineering]]></category>
            <category><![CDATA[scala]]></category>
            <dc:creator><![CDATA[Bertrand Junqua]]></dc:creator>
            <pubDate>Wed, 07 Oct 2026 08:16:25 GMT</pubDate>
            <atom:updated>2026-10-07T08:37:03.731Z</atom:updated>
            <content:encoded><![CDATA[<p><em>This article was written by a human, </em><a href="https://commons.wikimedia.org/wiki/File:Human_made_logo_by_Matt_Kindt.png#/media/File:Human_made_logo_by_Matt_Kindt.png"><em>without any help from generative AIs</em></a><em>.</em></p><p><em>TLDR; the search at Deezer was clogging up our cache systems. We significantly improved the situation by changing the serialization format of the cached payloads, and rolling out a tailor-made, space-efficient and blazing fast data structure inspired by Hash Array Mapped Tries.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hagsukOOXzGvfTFvoo8Ntw.jpeg" /><figcaption><em>Photo by </em><a href="https://pixabay.com/fr/users/jirreaux-14740159/"><em>Jirreaux Hiroé</em></a></figcaption></figure><p>Every time you type a character into the Deezer search box, a number of requests get fired against Query Mercury, our search service, to retrieve tracks, albums, artists, and many other types of items that make up the search results.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/806/1*J6HlUs01PoGiyAANenJRUw.png" /><figcaption>A seach for “Nicolas Petit” yields tracks, albums, playlists, artists, episodes, podcasts and user profiles</figcaption></figure><p>Query Mercury is a Scala app that was written in 2025, as part of an ongoing effort to pluck features out of the legacy PHP behemoth that still powers much of Deezer. It handles about three thousand requests per second on average, with peaks at five thousand every weekend and up to almost thirty thousand during holidays — most notably at midnight on New Years Eve.</p><p>With these numbers in mind, and given the fact that search should feel “instant” as you are typing, latency is of utmost importance to us. Currently, p50 latency hovers around 3.25 milliseconds across all endpoints, while p95 latency is about 42 milliseconds and p99, 60 milliseconds.</p><p>p50 latency basically reflects the latency of queries for which all calls to data stores were cached. For these, time is spent mostly waiting for Memcached and deserializing the payloads we receive from it.</p><p>As the code was being migrated, a significant amount of effort went into polishing the algorithms, tuning the garbage collection, and being friendly to the cats-effect scheduler to squeeze as much performance as possible from our hardware. Yet, around November of last year, as holiday season started looming upon us, a worrying trend of latency spikes began appearing. As it turns out, our work on performance had a significant blind spot.</p><h3>When Memcached sneezes, Deezer catches a cold</h3><p>Towards the end of 2025 our Infrastructure team started receiving concerning reports of big latency spikes from Memcached. These Memcached servers are the backbone of Deezer infrastructure and any sign of struggle from them will send ripples through pretty much every team.</p><p>What was making these servers near their maximal capacity was, of course, a combination of factors. However one of the main offenders appeared to be Query Mercury, in terms of both traffic (inwards and outwards) and average size of the values stored.</p><p>Our naive approach of throwing raw-ish JSON-serialized payloads into Memcached while paying little mind about their size or the load they were incurring was showing its limits.</p><h3>Low hanging fruit</h3><p>JSON is great of course, because of how widely supported it is, and because a serialized JSON payload remains human-readable. However, it is not a very efficient encoding in any other respect, nor does it try to be. JSON payloads are heavy, require the whole schema to be included in every datum, and their serialization/deserialization is generally quite slow.</p><p>The fairly low hanging fruit here was to swap JSON for something more efficient. In our use case, the schema for a given type of cache entry is always the same, and the service producing the data and the one consuming it are one and only: Query Mercury. Therefore, we do not need to serialize the schema alongside the data.</p><p>Simply put: &quot;foo&quot;:3,&quot;bar&quot;:&quot;baz&quot; can very well become 3,&quot;baz&quot; because we already know what comes in what order.</p><p>The second aspect is how the data itself is serialized: a JSON string will transform everything into UTF-8 characters, which while easily readable are never the most efficient way to encoding anything but a UTF-8 string.</p><p>A 32-bits integer such as 2147483647 blows up to 80 bits as a UTF-8. Similarly, a boolean that could be represented using a single bit becomes &quot;true&quot;, 32 bits, or &quot;false&quot;, 40 bits.</p><p>Binary encoding, on the other hand, packs as much data as possible in every byte, at the expense of readability. In our use-case, we do not need cached payloads to be readable, so it is a trade-off we were willing to accept.</p><p>Therefore, we picked a schema-less, binary format: <a href="https://avro.apache.org/docs/++version++/">Avro</a>. That simple change alone brought about a <strong>~54% reduction in the average payload size</strong>.</p><h3>Data structures nerd-out</h3><p>The result of a query on our database is typically an array of items which, in our case, can be an album, a song, an artist, a podcast, etc. Each item comes with their “country ranks”, a mapping of how popular this particular item is in every country where it has been consumed a minimum number of times.</p><p>We store this whole mapping alongside every item in the cache because it allows us to reuse the same cache entries for the same query made from different countries. Looking inside the Memcached payloads, it appeared that this mapping was making up a disproportionate share of the data we were storing.</p><p>Here’s what one item typically looks like, give or take some small attributes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yOci3icRS8bBV6wZxKoFew.png" /><figcaption>A payload from Elasticsearch. Most of it is the country ranks mapping.</figcaption></figure><p>A change in the way we represent this mapping — here, country_ranks_struct — was surely going to yield a significant reduction in the overall size of the data stored in Memcached.</p><p>If we were going to refine the way this data is laid out in memory (and later serialized), we first had to get a clear idea of:</p><ul><li>what constraints bound it (how big would the values get, could the keys be negative, etc.);</li><li>how we used it (for example, did we ever need to update it or iterate through it).</li></ul><p>This country_ranks_struct only contains distinct keys between 0 and 250 (Deezer country codes), and unsigned 32-bits integers as values. This map is retrieved from Elasticsearch and then only repeatedly queried (“what is the rank for country code xx?”). We never append to it, nor do we delete anything from it or update a value. Lastly, we do not need its serialized form to be readable by humans or even other apps.</p><p>In order to introduce the solution we eventually came up with, I will take you on a short tour of data structures that are traditionally used for such purposes and, most importantly, how one of them inspired us.</p><p>First, the venerable HashMap, which lays out the data in a large sparse array. Where each key/value pair goes is decided by computing hash(key) % sizeOfArray, which gives us the array index where the value will be written. In practice, collisions happen, meaning that the hash function will sometimes map two different keys to the same array index. There are several ways in which this is addressed (chaining, open addressing), but for the sake of simplicity we will assume no collisions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_iEELErGSWvBXtuSCPjqCg.png" /></figure><p>When a HashMap starts running out of space in the underlying array (the threshold is determined by its <em>load factor</em>), it will simply create a new, bigger array, and copy its data to it.</p><p>Such HashMaps are <em>mutable</em>, meaning that inserting, updating or deleting data will simply overwrite a slot in the original array. Functional languages, such as Scala or Haskell, tend to steer off mutable data structure because of the subtle, hard to predict and reproduce bugs they can introduce in multi-threaded environments. One solution would be to make a local copy of your HashMap every time you want to make a change to it. It would fix the concurrency issues, but it would have catastrophic performance characteristics, with every change taking Θ(n) time.</p><p>In order to provide data structures with similar APIs and performance characteristics to the venerable (but mutable) HashMap, functional programming languages had to get creative.</p><p>One of the solutions is the Hash Trie. It builds on the idea of a <a href="https://en.wikipedia.org/wiki/Trie">Trie</a>, but uses hash words instead of alphabet letters. In practice, it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vScv2m0O8IQa7M1tTGKxuw.png" /><figcaption>Basic hash trie holding value Foo, Bar, and Baz</figcaption></figure><p>What was the point of turning a simple, flat array into a more complex tree structure, one might ask. Well, now if we want to create a new version of this map where, for example, we deleted Bar, we would only have to copy the Root node to create a new version of it without a reference to the 0010 node it was pointing to.</p><p>Similarly, if we wanted to add a new key/value pair, we would only need to copy the nodes “on its path”. The Hash Trie thus gives us both immutability and Θ(log n) insertion/update/deletion time complexity. While it is not Θ(1) like the mutable array-backed HashMap, it is acceptable in practice.</p><p>There is, however, an annoying issue with the Hash Trie: it wastes a lot of space. To understand why, let’s look into its 1001 yellow node: it holds a sparse array big enough to contain all the possible values for, in this case, a 4-bits hash word.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6pT2cyRyUalAp2-aA0gn-w.png" /><figcaption>A sparse array in which the Hash Trie stores pointers to the node’s children</figcaption></figure><p>Each node holds as many slots for sub-nodes as there are possible values for the hash word. Our example here used very small 8-bits hashes but in practice, in order to minimize collisions, we will use much longer hashes. For a hash word of <em>m/h</em> bits, with m being the total hash length and h the maximum height of the tree, that means every node will hold an array of length 2^(m/h). The maximum number of nodes on the bottom level of a Hash Trie is then (2^(m/h))^h, and the maximum total number of nodes in a Hash Trie is then:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/143/1*QiiZjj-WKsK09bSFl81lcQ.jpeg" /></figure><p>In this formula, the base is what determines how much space the tree will waste. Here it is 2^(m/h), and that will always end up being a very large number to avoid hash collisions.</p><p>To illustrate this, let’s consider a Hash Trie that uses 64-bits hashes, split into 8-bits words. Our tree will be of height 8 (64/8), each node will hold a 256 items-long array (2^8) and will require up to 512 000 nodes to hold 250 items (250*256*8), assuming worst-case hash distribution. Generally, the less data it holds, the more a Hash Trie will waste space.</p><p>The problem here is all too obvious: this simple implementation of a Hash Trie is simply too wasteful to be practical. This is why it was improved upon by the Hash Array Mapped Trie, which incidentally is the default immutable Map implementation in Scala.</p><p>From a distance, a Hash Array Mapped Trie (HAMT) looks exactly like a Hash Trie, however if we look inside a node, we see a neat little trick at play. Here’s the same node as before:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*atwi-6YJU8l8wtETi7wt-A.png" /><figcaption>The same node as before, now holding the pointers in a dense array along with a bitset</figcaption></figure><p>It’s not exactly straightforward, so let’s break this down. In the Hash Trie equivalent of this node, we had a sparse array with data in the 6th and 11th spot, corresponding to the hash words of the data these were pointing to (0110 and 1011, respectively).</p><p>In the HAMT node, we’ve taken that sparse array and made it dense: the two pointers are stored contiguously in a 2-items-long array. The way we make this work is by storing a bitset alongside it, in order for us to be able to find what is what in that dense array. When, in the Hash Trie, we would simply query the 11th index to get the node pointing to Foo, here we first look it the bitset and count the number of 1s up to the 11th bit. That’s 2, so we know we need to retrieve the second element from the array.</p><p>Now let’s reuse that same trick to store country codes and ranks instead of hash words and pointers:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jaS_jgzLqwLujQ9CLVnq8Q.png" /><figcaption>The same as a HAMT node, now holding country ranks instead of pointers to other nodes</figcaption></figure><p>Now we just need to tweak it a bit in order to store keys up to 250, so the bitset becomes an array of up to four 64-bits integers, since 4*64 = 256, and there we have our Mapped Bitset!</p><p>The keys are stored in very compact form, and the values are in a simple, dense array. Serialized size, on a full “country ranks” map, is therefore reduced drastically, with the Mapped Bitset being <strong>43% smaller</strong>.</p><p>Not only is the serialized size lower, but we get additional benefits from using a more compact representation of that same data. The in-memory size of a 250-keys Mapped Bitset (1096 bytes) is <strong>more than 93% smaller</strong> than the equivalent HAMT (15896 bytes) and <strong>94% smaller</strong> than the HashMap (18160 bytes), and thus puts much less pressure on the garbage collector.</p><p>The CPU cache-friendly bitset and dense values array, along with the use of the <a href="https://en.wikipedia.org/wiki/SSE4#POPCNT_and_LZCNT">POPCOUNT</a> instruction to faster count “<a href="https://en.wikipedia.org/wiki/Hamming_weight">the number of 1s up to</a>”, allow for lookup speeds significantly faster than the HAMT, and even slightly faster than the mutable HashMap.</p><pre>Benchmarks.HAMTLookup           thrpt    124,215   ±   9,273  ops/us<br>Benchmarks.HashMapLookup        thrpt    536,493   ±  70,561  ops/us<br>Benchmarks.MappedBitsetLookup   thrpt    554,274   ±  29,708  ops/us</pre><p>JSON serialization and deserialization of the country ranks, which used to make up a significant proportion of the CPU time spend by production pods, is also dramatically sped up by the Mapped Bitset, since we serialize it “as is” (two dense arrays) instead of transforming it into the usual human-friendly readable JSON object. Comparing serialization and deserialization speed to HashMap and HAMT is therefore like comparing apples to oranges, but serializing a data structure “as is” is in itself a valid optimization technique, when the layout of the data structure allows it and you do not need the serialized result to be human-readable.</p><p>JSON serialization of a Mapped Bitset using <a href="https://github.com/plokhotnyuk/jsoniter-scala">jsoniter-scala</a> is 8 times faster than a HAMT and 2.4 times faster than a HashMap. Deserialization is 3 times faster than a HAMT and 3.5 times faster than a HashMap.</p><h3>Putting it all together</h3><p>We then put these two optimizations together and evaluated the overall change in Memcached payload size for Elasticsearch data: local tests showed a <strong>74% overall reduction</strong> in the average size of these payloads. We were very happy with these results given that no compromise was made — the data was exactly the same, just represented differently. It was time to release it in production.</p><p>The results were quite dramatic. Here is the impact on overall Query Mercury, a ~55% drop in <em>overall</em> bandwidth usage:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i1ifPmVxiFA4wr44h5f8FQ.jpeg" /><figcaption>A graph of overall Query Mercury bandwidth, showing a ~55% overall drop</figcaption></figure><p>The release of both our optimizations also had a significant impact on the Memcached fleet, with more than 60 gigabytes freed:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*GLhim9ftEuhwDyv6gX0gmA.jpeg" /><figcaption>A graph of Memcached RAM usage, showing a 60GB reduction</figcaption></figure><p>And finally, the shared Memcached bandwidth also dropped significantly:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*GSLdH3KbEt8LttJ5X1E2rA.jpeg" /><figcaption>A graph showing a significant drop in overall Memcached bandwidth</figcaption></figure><h3>Wrapping up</h3><p>The Query Mercury code base is layered in such a way that adding remote cache to a function call amounts to wrapping a higher-order caching function called wrapUnaryAsync around it. The purpose of this abstraction is to allow the contributor to just slap it on an expensive call and forget about it. It will generate a key from the argument, serialize the payload, handle errors, fall back gracefully when the caching back-end is inaccessible, etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/612/1*JW4ricim3y11Go0KZAd5mg.png" /><figcaption>A higher-order function adding remote cache to a function call</figcaption></figure><p>What this experience taught us is that when working at this scale, you can’t <em>really</em> slap cache on top of anything and forget about it. Of course cardinality and hit ratios have to be monitored, but so do the payload sizes. The law of <a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/">leaky abstractions</a> remains undefeated.</p><p>Another lesson this taught us is that data structures are fun (nah, we knew that already) and that knowing their implementation details is an absolute necessity when treading on your application’s hot path. And if your use case is narrow enough, you might have to whip up your own to get the most out of your CPU.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e2bd5fd65e0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/how-we-slashed-the-size-of-our-search-cache-by-74-4e2bd5fd65e0">How we slashed the size of our search cache by 74%</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Workflow or agent? What building QAradar taught us]]></title>
            <link>https://medium.com/deezer-engineering/workflow-or-agent-what-building-qaradar-taught-us-7beec702b574?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/7beec702b574</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[ai-workflow]]></category>
            <category><![CDATA[agentic-ai]]></category>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[ai-agent]]></category>
            <dc:creator><![CDATA[Victoria Alajemba]]></dc:creator>
            <pubDate>Wed, 09 Sep 2026 10:09:25 GMT</pubDate>
            <atom:updated>2026-09-09T10:09:24.646Z</atom:updated>
            <content:encoded><![CDATA[<h3>The problem we set out to solve</h3><p>At Deezer, the Quality Assurance (QA) team takes real pride in a reliable, well-maintained automated test suite. We write these tests to ensure the features we ship actually behave as intended. But for our consumer-facing applications, that coverage traditionally lived with QA and nowhere else.</p><p>Through interviews across our Android, iOS, and back-end teams, we half-expected to hear that tests were too slow, untrusted, or simply not worth the bother. Almost without exception, the message was the same: discoverability was the real blocker. Because developers had only partial, awkward access to the test suite, there was no reliable map linking the feature someone was building to the tests that already covered it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U9o9kUQnnM26ba279W1aEg.png" /><figcaption>Knowledge Gap. Adapted from Bridging the Physioswiss “Know-Do Gap” through Implementation Science</figcaption></figure><p>That gap created constant, small frictions that showed up in three recurring ways:</p><ul><li><strong>A plain lack of visibility: </strong>Developers usually couldn’t tell which automated tests already touched their feature. This meant regressions slipped through, validation carried a nagging sense of uncertainty, and people occasionally wrote tests that quietly duplicated ones we already had.</li><li><strong>Knowledge silos and bottlenecks:</strong> Knowledge traveled through relationships rather than tools. The only way to get a definitive answer was to interrupt one of a small number of QA experts who held the knowledge in their heads. That back-and-forth was slow for the developers waiting on an answer and repetitive for the QA engineers giving it.</li><li><strong>Painful execution: </strong>Even when a developer knew which test they wanted to run, actually running it was a chore. The jobs in our CI system were awkward to navigate, full of stale links, and required insider knowledge to find.</li></ul><p>What tied these issues together was that knowledge lived in people and shared memory rather than in anything a developer could query on their own. Over time, that settled into one of those quiet misalignments that nobody really owns but everybody feels.</p><p>In every single interview, one unprompted request surfaced: developers wanted test information to live where they already worked, not in yet another separate tool they’d have to remember to open. The solution had to exist in an all-day workspace and require little to no effort. We made that an uncompromisable constraint because a genuinely helpful tool that sits outside the user’s comfort zone is a tool that, in practice, nobody opens.</p><p>That raised an obvious question: how do we make our tests visible to developers in a way that lets them see what is being tested and rerun the relevant tests themselves to verify a fix, without routing every request through a person? Our answer to this question is QAradar.</p><h3>QAradar: the solution and the architecture</h3><p>Before going any further, it helps to be precise about the word “agent,” because it has been stretched to cover almost anything, from a chatbot with a good memory to a system that runs unattended for hours at a time. The distinction between agents and workflows that we’ve found most useful in practice is an architectural one rather than a question of how sophisticated a system feels. A workflow is a system in which the steps are orchestrated through predefined code, so that you lay out the path in advance and the model fills in the specific pieces you’ve asked it to. An agent, by contrast, is a system in which the model directs its own process, deciding for itself what to do next and which tools to use in order to reach a goal you’ve handed it.</p><p>The reason this matters is that the line isn’t really about capability or polish, since a workflow can be extremely capable and still be a workflow; it’s about who is steering. What makes something an agent is that you’ve given the model the wheel and let it choose the route.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JygrbXYeh1mxqeynX1rd4Q.png" /><figcaption>A workflow runs along a fixed path in advance; an agent decides its next step and loops on feedback. QAradar is deliberately the left-hand pattern. Adapted from @VirtuOasis’s Agents vs. Workflows explainer on HuggingFace</figcaption></figure><h4>What QAradar actually does</h4><p>The path QAradar follows is a fixed workflow that comes down to four steps:</p><ol><li>It begins when a developer tags @QAradar in a conversation on the platform.</li><li>It runs a short intake, asking for the developer’s role, platform, and feature; this is essential to ensure that a back-end engineer and an iOS engineer working on the same screen aren’t handed the same undifferentiated list.</li><li>It searches a prepared catalog of tests and matches the relevant ones based on features, platforms, and tags.</li><li>It returns those tests, complete with method names, source files, and the exact parameters the developer needs to run them.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*ElfY7Cb9VkxUay1OUsJS2Q.png" /><figcaption>TechQAradar workflow</figcaption></figure><p>From there, the developer takes the parameters and runs the tests with no message to QA required. At no point does the model decide for itself what to do next; it follows the steps we laid out and then stops.</p><h4>How the catalog is built</h4><p>The catalog that QAradar searches isn’t read live from the codebase; we generate it in advance on a schedule. A relatively simple JavaScript program walks through our test files, parses the annotations, and extracts the method name, description, environment, and feature area. It writes this out as plain Markdown, producing one catalog file per platform, along with a context file that tells the model how to read them.</p><p>The striking thing about this is how ordinary it is. The intelligence that makes the answers good isn’t in the model at all; it’s in the discipline of turning scattered, inconsistent test code into a clean, well-structured document. Keeping that catalog trustworthy is the genuine work, and the model sitting on top of it is very nearly the easy part.</p><h3>Why we chose a workflow</h3><p>It’s tempting to treat “agent” as the finish line and “workflow” as a half-finished prototype, but QAradar is a workflow by deliberate design. Matching a feature to its tests is a well-defined task with a predictable shape. Handing the model control over its own retrieval process would buy us nothing except more latency, higher cost, and more opportunities to fail.</p><h4>The road to agentic execution</h4><p>While we deliberately built QAradar as a workflow, it’s worth thinking about what it would take to give it agentic “superpowers”, if only because the exercise forces us to reason carefully about context and access, two core requirements for any real-world AI tool. The progression from workflow to agent isn’t really a modeling problem; it’s largely a matter of progressively adding data, perception, action, and feedback.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IFsRNVxH7nKP9kS6yTU3Kg.png" /><figcaption>The four layers between a workflow and an agent</figcaption></figure><h3>What we learned</h3><p>Building QAradar was about more than saving a few minutes of back-and-forth. It removed a dependency for developers, handing time back to the QA team for higher-value work. It helped foster a culture where quality knowledge is visible rather than locked in silos.</p><p>For other teams looking to build something similar, here are our four biggest takeaways:</p><ul><li><strong>Name the architecture honestly:</strong> Simplicity is a feature, not a shortfall. Most “agents” are actually workflows, and that is usually the right call. Autonomy is only worth pursuing when a task genuinely requires open-ended judgment.</li><li><strong>Integrations dictate capability:</strong> The line you can’t cross is usually system access, not model intelligence. Before asking how clever a tool can be, ask what internal APIs it is actually allowed to touch.</li><li><strong>Meet people where they work:</strong> The strongest signal from our interviews was that a tool behind a new login gets ignored. If it doesn’t live in the developer’s existing workspace, it won’t be used.</li><li><strong>Ship the workflow first:</strong> A scheduled, honestly limited workflow that removes daily friction easily beats an ambitious, autonomous agent that doesn’t exist yet. You can earn your way to deeper integrations once the workflow proves its worth.</li></ul><p>QAradar isn’t an agent by intention. Choosing the right level of autonomy mattered more, in the end, than reaching for the most ambitious architecture we could have drawn on a whiteboard.</p><h3>Acknowledgements</h3><p>QAradar was designed and built by Elias Amour during his internship with the team, and we’re grateful to him for taking on the project.</p><h3>References</h3><ul><li><a href="https://www.anthropic.com/engineering/building-effective-agents">https://www.anthropic.com/engineering/building-effective-agents</a></li><li><a href="https://www.louisbouchard.ca/blog-ia/agents-ou-workflows">https://www.louisbouchard.ca/blog-ia/agents-ou-workflows</a></li><li><a href="https://mer.vin/2026/05/when-not-to-build-ai-agents-anthropics-workflow-vs-agent-playbook/">https://mer.vin/2026/05/when-not-to-build-ai-agents-anthropics-workflow-vs-agent-playbook/</a></li><li><a href="https://huggingface.co/blog/MengkangHu/workflow-vs-agent">https://huggingface.co/blog/MengkangHu/workflow-vs-agent</a></li><li><a href="https://huggingface.co/blog/VirtualOasis/agents-vs-workflows-en">https://huggingface.co/blog/VirtualOasis/agents-vs-workflows-en</a></li></ul><p><em>Learn more about Victoria Alajemba, intern at Deezer and author of this article, on </em><a href="https://fr.linkedin.com/in/alajemba"><em>LinkedIn</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7beec702b574" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/workflow-or-agent-what-building-qaradar-taught-us-7beec702b574">Workflow or agent? What building QAradar taught us</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Forum PHP 2025, a year of anniversaries]]></title>
            <link>https://medium.com/deezer-engineering/forum-php-2025-a-year-of-anniversaries-d54263366fd2?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/d54263366fd2</guid>
            <category><![CDATA[symfony]]></category>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[conference]]></category>
            <category><![CDATA[forum-php]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Sébastien COURJEAN]]></dc:creator>
            <pubDate>Wed, 19 Nov 2025 10:35:46 GMT</pubDate>
            <atom:updated>2025-11-19T10:35:44.843Z</atom:updated>
            <content:encoded><![CDATA[<p>This year was the 4th edition of Forum PHP, organized by the AFUP (Association Française des Utilisateurs de PHP or the French Association of PHP Users), at Disneyland Paris’ Art of Marvel Hotel. Over the two days of the conference, different topics celebrated the anniversaries of: PHP (30 years of existence), AFUP (25), Symfony (20), and API Platform (10).</p><h3>Our favorite conferences</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hVzEdKljIKP3DHilYGSl9Q.jpeg" /><figcaption>Picture by AFUP</figcaption></figure><p>We’ve put together a small selection of our favorite topics from the conference. All talks will be available on replay from December 1st on the <a href="https://www.youtube.com/@afupPHP">AFUP YouTube channel</a> in the form of an Advent calendar.</p><h4>Day 1</h4><p><strong>“Optimisation de la pagination multi-source pour le suivi des virements”</strong><br>by Mathieu DESNOUVEAUX, BoursoBank</p><p>Mathieu Desnouveaux, a senior developer at BoursoBank, discussed how they manage multi-source pagination for tracking money transfers.</p><p>The challenge arises from the fact that Boursobank collaborates with multiple partners, each providing data in a different format. All of these transfers need to be shown together in a single, unified list, without revealing the source of the data.</p><p>At the beginning of his talk, Mathieu shared a personal anecdote: as a child, his grandmother used to tell him to “never mix dish towels and napkins”. He compared this to combining transfers from different sources and demonstrated, through his work, that it is possible to mix them while maintaining organization and coherence. It was such a clever and memorable analogy that made the technical content more relatable.</p><p>Mathieu explained how BoursoBank uses an encrypted cursor as the backbone of their solution. This cursor keeps track of the transactions that have already been shown and allows the system to fetch the next page while hiding the underlying complexity.</p><p>The MultiSourcePaginator service collects data from all sources, consolidates it, and sorts it chronologically, ensuring users always see their most recent transfers first. If a source runs out of data, it is removed from the process. If new data appears, it is seamlessly integrated.</p><p>Performance is also key. They use Redis-caching to avoid overloading their partners with repeated calls. The cache is updated or invalidated in real time whenever transfers are added, modified, or canceled.</p><p>What we really liked about the talk is how Mathieu tackled such a complex problem in a clear and structured way. The approach is smart and practical, and we could imagine encountering a similar situation in our own work.</p><p>Overall, the talk provided us with both technical insight into multi-source pagination and an understanding of the careful planning required to make large-scale banking systems function smoothly.</p><p><strong>“Créer un serveur MCP avec Symfony”</strong><br>by Edouard COURTY, Sézane</p><p>Édouard Courty began his talk by reviewing the major milestones in the evolution of artificial intelligence. From IBM’s Deep Blue in 1996, the publication of Google’s paper <em>“Attention is All You Need”</em> in 2017, GPT-3.5 in 2022, and finally, GPT-5 in August 2025. Édouard highlighted how rapidly AI is advancing, noting that updates to Large Language Models (LLMs) and AI tools are now occurring at breakneck speeds.</p><p>However, despite the remarkable progress of AI systems in reasoning, analysis, and knowledge generation, LLMs are still not capable of performing actions directly. For example, if you ask an AI to create a playlist on your favorite service (most likely Deezer!), it will result in the LLM suggesting tracks, but it will not be able to create the playlist within the application itself.</p><p>The main issue is that LLMs are confined to their environment and have no access to external tools. There is, however, a solution: the <strong>Model Context Protocol (MCP)</strong>. MCP is a communication protocol developed by Anthropic, designed to enable AI to interact with servers that provide a range of tools for performing various actions.</p><p>It operates on a client/server architecture, communicating via Streamable HTTP or STDIO, and exchanges JSON-RPC messages.</p><p>In summary, the MCP lifecycle can be broken down into three phases:</p><ul><li><strong>Discovery and connection</strong>: The initial exchange between the AI-client and the server to share information, such as versions. The server exposes its available capabilities to the client.</li><li><strong>Negotiation and handshake</strong>: Exchanging information about versions and capabilities to ensure the client and server can communicate correctly.</li><li><strong>Usage</strong>: The AI utilizes the tools provided by the server to execute the actions requested by the user.</li></ul><p>Édouard presented his <a href="https://github.com/EdouardCourty/mcp-server-bundle">mcp-server-bundle</a>, which makes it easy to implement an MCP server in Symfony.</p><p>The bundle handles transport and the communication protocol, leaving it to the user to integrate tools via services. Édouard demonstrated a scenario where a user interacts with a chatbot to place an order or update their information in natural language, while the AI, through the MCP server, updates the database and orchestrates the necessary actions on connected tools.</p><p>While the experience is impressive, it quickly highlights some important limitations. In fact, it was the first question asked to Édouard after his presentation: <em>“</em>What about security?<em>”</em> Indeed, the reliability and security of the data is not fully guaranteed, since it comes directly from users. Additionally, the AI must continually determine which tool to use for each request, thereby carrying the risk of errors or unintended actions. The presence of multiple connected tools can also destabilize the system, and as an LLM, it remains prone to hallucinations, which may result in unpredictable or inconsistent behaviors.</p><p>Fortunately, the MCP protocol is new and evolving rapidly. For example, OAuth 2 authentication has recently been implemented, which should improve the security related to user permissions and available actions.</p><p>Édouard also mentioned that Symfony recently released its <a href="https://github.com/symfony/ai">own bundles</a> for integrating AI into Symfony projects. These bundles are still in development and relatively new, but they are expected to evolve rapidly with community support. Be sure to explore them and, if possible, contribute to their growth and development.</p><p><strong>“What’s new in PHP 8.5”</strong><br>by Derick RETHANS, XDebug</p><p>With the upcoming release of PHP 8.5, now is a great time to review the new features that will be introduced. Derick, the author of XDebug and a long-time contributor to PHP, provided valuable insights into the upcoming enhancements and context for each improvement. Among the notable new features are:</p><ul><li>The new pipe operator (|&gt;), designed for functional programming;</li><li>OPCache will no longer be optional and will be enabled by default;</li><li>The ability to use closures in constants.</li></ul><p>If you appreciate immutability patterns, you will particularly enjoy the following additions:</p><ul><li>The “clone with” operator simplifies the creation of modified copies of immutable objects;</li><li>The NoDiscard attribute, which helps users remember to use the created copies.</li></ul><p>Derick also pointed out some interesting improvements for the parse_url and grapheme-related functions. However, these will be introduced through new functions, as changing the behavior of existing functions that are heavily used can be challenging.</p><p><strong>“Quatre patterns avancés pour améliorer la résilience d’une application”<br></strong>by Pascal MARTIN, Principal Engineer at Bedrock</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j9BcNBhLefxlSXrsH424Kw.jpeg" /><figcaption>Picture by AFUP</figcaption></figure><p>During the conference, Pascal Martin, Principal Engineer at Bedrock, gave a talk on resilience patterns for applications. He explained that every system eventually experiences failures — slow APIs, cold caches, or sudden traffic spikes — exposing the hidden fragility of even “stable” systems. To address this, he presented three practical patterns.</p><p><strong>Random Jitter Exponential Backoff<br></strong>Immediate retries after failures can exacerbate problems if all clients attempt to retry simultaneously. Pascal explained that exponential backoff, where retry intervals grow longer with each attempt, combined with a bit of randomness (jitter), spreads retries and prevents synchronized spikes. He shared examples, such as cron jobs all running at 4 a.m. or cache entries expiring simultaneously, which can overload databases. Randomizing retries or cache expirations helps smooth the load and reduce cascading failures.</p><p><strong>Constant Work<br></strong>Traffic peaks are unavoidable, and systems generating responses on demand can struggle. The Constant Work pattern involves performing a small amount of work continuously, even during periods of low demand, by precomputing responses or refreshing caches. This approach keeps queues short, responses fast, and load predictable. In essence, it prepares the system in advance rather than reacting to spikes.</p><p><strong>Shuffle Sharding<br></strong>Even well-designed systems can be impacted by a single noisy client. Shuffle Sharding isolates failures by splitting the infrastructure into independent “cells” — such as servers, clusters, or data centers — and assigning client groups to unique subsets. Pascal provided examples such as assigning specific servers to client groups or ensuring clients never share the same data center. This containment prevents failures in one group from affecting others.</p><p><strong>Key takeaways</strong></p><ul><li>Random Jitter Exponential Backoff: Spreads retries to avoid synchronized spikes.</li><li>Constant Work: Precomputes responses to reduce latency and fragility.</li><li>Shuffle Sharding: Isolates failures to limit impact on other clients.</li></ul><p>According to Pascal, resilience isn’t magic. It comes from small, deliberate design decisions. Implementing these patterns can dramatically improve system behavior during traffic spikes, failures, or other unexpected events.</p><h4>Day 2</h4><p><strong>“Perf et injection de dépendances — êtes-vous suffisamment paresseux·ses ?”<br></strong>by Nicolas GREKAS, Symfony SAS</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t2Hk0urFnB914x8s0e_eqQ.jpeg" /><figcaption>Picture by AFUP</figcaption></figure><p>The talk started with a quick reminder of the SOLID principles: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion.</p><p>Then, Nicolas discussed dependency injection through interfaces, AutoconfigureTag, and AutowireIterator attributes. All of this generates a static PHP file called a container, which will be used by the runtime process to inject those dependencies.</p><p>Through the example of implementing a timer on a channel feature, we then discovered the decorator pattern and a multitude of attributes that enable its integration.</p><p>Warning: Listener events can cause poor performance if multiple dependencies are cascaded. It provided Nicolas the opportunity to discuss a solution to this problem using the concept of lazy on dependencies via the Lazy / Autowire / AutowireMethodOf / AutowireLocator attributes.</p><p><strong>“FrankenPHP en dehors des sentiers battus”<br></strong>by Kévin DUNGLAS, Co-Founder at Les-Tilleuls.coop</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j72Ueq4DSYFDiAVHGAD54Q.jpeg" /><figcaption>Picture by AFUP</figcaption></figure><p>Kévin quickly reintroduced us to FrankenPHP, a modern app server with easy deployment using Docker or a binary. It offers many features, such as HTTPS, 103 Early Hints, HTTP3, Logs, Monitoring, and includes a Go WebServer called Caddy.</p><p>The binary application contains Caddy, PHP with its dependencies, the application source, and assets.</p><p>One of the most interesting features presented is the worker mode. It is booted once and then stored in the memory, which means we don’t need any further initialization on each request, and it offers a real performance gain. Kévin warned us that it only works if the app is stateless and has no existing memory leaks, though.</p><p>Kévin concluded his talk by presenting a new feature: Go workers. It allows Go and/or PHP scripts to be run asynchronously, and also enables gRPC support and the development of custom Go extensions within FrankenPHP.</p><p><strong>“10 ans de Clean Architecture chez OpenClassrooms : le rêve, la réalité, les leçons”</strong><br>by Olivier MAIRET, OpenClassrooms</p><p>Olivier shared a real behind-the-scenes view of the OpenClassrooms technical team, showing how they gradually transformed a tangled, hard-to-maintain codebase into a stable, testable, and evolvable system using principles inspired by Clean Architecture.</p><p>Initially, their 2013 rewrite using Symfony failed, despite the use of modern frameworks; the code remained fragile and interdependent. This led them to refocus on structure over framework, isolating business logic from technical layers, and organizing code with controllers, use cases, entities, gateways, and builders.</p><p>The first implementation brought stability and testability, but the team realized that rigidly following architectural principles made the system too heavy for everyday needs. They therefore refined and streamlined the architecture, utilizing a generic controller, simpler Data Transfer Objects (DTOs), and simplifying their use cases, while balancing discipline with pragmatism.</p><p>We appreciated seeing a real team reflect on the trade-offs between strict architecture and practical development — something we’ve personally experienced as engineers. It was refreshing to see a talk that combined technical insights with reasoned reflection, not just dogma.</p><p>Overall, the talk was inspiring, demonstrating how the thoughtful application of Clean Architecture can create code that is robust, maintainable, and flexible over a decade of growth.</p><p><strong>“Symfony UX LiveComponents : construire un dashboard interactif sans une ligne de JS”<br></strong>by Felix EYMONOT, PHP and Symfony Expert at Bludevon</p><p>This conference featured a live demo of UX LiveComponents, a Symfony component developed by Felix Eymonot himself. This component provides the needed JavaScript boilerplate code to easily render model objects from the backend to the frontend view.</p><p>Throughout the presentation, Felix used the example of a developer who is asked by the Product team to create an invoice dashboard for their company — first an MVP, then a series of increasingly complex improvements. The aim of the presentation was to show that, in simple cases, the component enables developers to add interactive, real-time features quickly and easily, without writing any JavaScript code.</p><p>The principle involves adding attributes to PHP objects and directives to the Twig template to enable UX LiveComponents to perform a ‘mapping’ between the data and the view.</p><p>This allows the developer to:</p><ul><li>Paginate,</li><li>Refresh the view in real time,</li><li>Display the data in different ways (e.g., table or graph),</li><li>Filter and sort the data.</li></ul><p>Finally, Felix listed the limitations of the component.</p><p>It is only suited to:</p><ul><li>100% Twig developments,</li><li>Quite simple projects, such as dashboards or CRUD back offices.</li></ul><p>It is not suited to:</p><ul><li>Real-time applications,</li><li>Offline mode,</li><li>Complex animations.</li></ul><h3>Beyond the conference</h3><p>The conference was a great occasion for the ten of us who attended the event to spend time together — which we don’t do that much otherwise, as we are part of different teams at Deezer — and meet others in the community.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*75aJJjzIwv5GNtIvBOnZ4Q.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1r6co_rNU3-WHjfaT2MvFg.jpeg" /></figure><p>We had numerous discussions with conference attendees over the two days, discussing Deezer features like Flow, Shaker, and technical architecture.</p><p>We also organized four music quizzes (two per day) at our stand. Everyone loved it, and we all had a great time. And of course, every participant walked out with a nice Deezer-branded goodie!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jFKmrYJfX2Yg3mfz9GZh9A.jpeg" /></figure><p>This year, we also introduced something new during the lunch break, with the presentation of our new contest feature, Deezer Club, by Benoit Viguier. It was a real success in terms of both product and technical exchanges, so we plan to repeat this format next year with another Deezer feature.</p><h3>In a nutshell</h3><p>With around 40 talks and more than 700 attendees over two days of conferences, the AFUP sure knows how to celebrate anniversaries! Once again, we had a great time meeting and exchanging with the PHP community, and we look forward to seeing everyone there again next year!</p><p><a href="https://www.deezerjobs.com/en/">Ready to rock? - Deezer Jobs</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d54263366fd2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/forum-php-2025-a-year-of-anniversaries-d54263366fd2">Forum PHP 2025, a year of anniversaries</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GraphQL JIT, is it worth it?]]></title>
            <link>https://medium.com/deezer-engineering/graphql-jit-is-it-worth-it-64e66f21dbb8?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/64e66f21dbb8</guid>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[performance-optimization]]></category>
            <category><![CDATA[graphql]]></category>
            <dc:creator><![CDATA[Ztec]]></dc:creator>
            <pubDate>Wed, 29 Jan 2025 09:28:45 GMT</pubDate>
            <atom:updated>2025-01-29T09:28:45.541Z</atom:updated>
            <content:encoded><![CDATA[<h3>Backstory</h3><p>A few years ago, I designed and set up a GraphQL server at Deezer. Among all the challenges I faced, one was related to performance. Designing a GraphQL server that is both convenient to use and performant is not an easy task, especially in Node.js.</p><p>One of the optimizations we made with <a href="https://fr.linkedin.com/in/jimmythomasinfo">Jimmy Thomas</a> was to use <a href="https://github.com/zalando-incubator/graphql-jit">GraphQL JIT</a> instead of the default GraphQL execution engine.<br>The <a href="https://github.com/zalando-incubator/graphql-jit?tab=readme-ov-file#why">README</a> claims to improve response time and increase the number of queries that can be executed per second by reducing the CPU time it takes to compute a single query.</p><h3>What is GraphQL JIT</h3><p>GraphQL JIT is a Just-In-Time compiler for GraphQL queries. It is designed to take advantage of <a href="https://en.wikipedia.org/wiki/V8_(JavaScript_engine)">V8</a> optimizations to increase performance. It is a drop-in replacement for the default execution engine of GraphQL, with a few restrictions.<br>One of the most important is stated in the project’s README:</p><blockquote>All computed properties must have a resolver, and only these can return a Promise.</blockquote><p>Depending on how you design your server, this limitation may or may not be an issue.<br>In my case, it was not an issue at all.</p><p>The performance gain claim is pretty impressive: up to 10 times faster than the default execution engine.</p><h3>Why we switched</h3><p>At the time, we conducted extensive tests on the project and used <a href="https://en.wikipedia.org/wiki/Gatling_(software)">Gatling</a> for load testing. I specifically crafted some reference queries and used them to compare the performance of all the changes made. This way, we could improve response time and admissible load bit by bit. One of the most important changes was the adoption of GraphQL JIT. The performance gain was substantial enough to make it worth the switch at the time.<br>Unfortunately, all reports and graphs were purged, thanks to Jenkins’ cleaning policies.</p><h3>Why reconsider it now?</h3><p>When we conducted the tests and made the switch, the project was barely used. I completely relied on my test queries for benchmarks. Those queries were built to be representative of a theoretical usage, but they were not real queries as no clients of the API existed yet.</p><p>Now, the API is live and in use. I have real-world queries to analyze, and I can observe the server’s performance in real-time with real clients.</p><p>So, let’s revisit this choice and see if it is still the right one.</p><h3>Test methodology</h3><p>Two tests are in order:</p><ul><li>One in production with real customers queries</li><li>One using the old method with Gatling, as before</li></ul><h4>Production test</h4><p>To test the two engines simultaneously, I modified the server’s code to randomly start with one engine or the other. When deployed onto the Kubernetes cluster, multiple instances of the service will be started — enough from both engines to gather significant statistics.</p><p>The code is quite barbaric but works wonderfully:</p><pre>expressApp.use(<br> […],<br> Math.random() &gt; 0.5<br> ? createJitGraphqlMiddleware({schema})<br> : createJsGraphqlMiddleware(schema),<br>);</pre><p>Once in production, I will analyze metrics to evaluate the impact of each engine. I will primarily focus on:</p><ul><li>System metrics such as CPU and memory usage</li><li>Node.js system metrics such as <a href="https://nodesource.com/blog/event-loop-utilization-nodejs/">Event Loop Utilization (ELU)</a>, HEAP size, and Garbage Collection</li><li>Response time goals (how much it deviates from the target)</li><li>Average response time and 95th percentile</li></ul><h4>Lab tests</h4><p>For the lab tests, I will use what’s already available from my previous tests years ago. I’ve set up Gatling with a few typical queries. The queries and rate of fire are not exactly the same as before because, over the years, I’ve tuned them to be more representative of the real world. When I say “more representative,” I don’t mean they are truly representative. Instead, I’ve simply adjusted them based on our observations in the field and modified them in a way that makes them closer to the real world. Unfortunately, I have no way of confirming this; it’s far from scientific here.</p><p>The most notable change is the way scenarios are grouped. Previously, each query had its own rules during the test. Now, I’ve grouped them into two main categories, representing two typical client profiles:</p><ul><li><strong>Light users</strong> send small queries. This is standard usage of the API, similar to how any customer would interact with our apps/front-end.</li><li><strong>Heavy users</strong> make large and complex queries with lots of fields and nested fields. This is typical of a customer using some costly features of our apps or any rogue client trying to abuse the API.</li></ul><p>What’s important is that the two profiles do not fire queries at the same rate. One fires far more frequently than the other.</p><p>The final Gatling scenario looks like this:</p><pre>val heavyUser_ConcurentUser = max_reqps/20<br>val heavyUser_ConcurentRequest = max_reqps/20<br>val lightUser_ConcurentUser = max_reqps - heavyUser_ConcurentUser<br>val lightUser_ConcurentRequest = max_reqps - heavyUser_ConcurentRequest<br>setUp(<br> lightUser.inject(<br> rampConcurrentUsers(1) to (lightUser_ConcurentUser) during (2 minutes),<br> constantConcurrentUsers(lightUser_ConcurentUser) during (duration - 2 minutes)<br> ).throttle(reachRps(lightUser_ConcurentRequest) in (duration minutes)),<br> <br> heavyUser.inject(<br> rampConcurrentUsers(1) to (heavyUser_ConcurentUser) during (2 minutes),<br> constantConcurrentUsers(heavyUser_ConcurentUser) during (duration - 2 minutes)<br> ).throttle(reachRps(heavyUser_ConcurentRequest) in (duration minutes))<br>).protocols(httpProtocol)</pre><p>The Gatling tests will run on my laptop but will target instances of the server deployed in a development Kubernetes cluster. This cluster is similar to the one in production in form and configuration but not in size. I won’t push the cluster to its limits, so I’m not concerned about the results being skewed by the environment.</p><p>I will monitor the same metrics as in the production test. This time, I will not consider the results from Gatling itself because I am running the tests from my laptop and cannot trust the response times gathered by Gatling. Moreover, I don’t really need them.</p><h3>Results</h3><h4>Production results</h4><p>— <strong>Response times</strong></p><p>One of the metrics I have is the time it takes for the engine to compute a request and produce a response.<br>It does not include the time it takes to send the response to the client or network delays.</p><figure><img alt="Graph of the average response time per engine" src="https://cdn-images-1.medium.com/max/1024/1*G4JzYCEklJrvbqxwWEWqSA.png" /><figcaption>Average response time per engine</figcaption></figure><p>The average response time shows that the JS engine is slightly faster, but with only a one-millisecond difference, it’s not really significant.</p><figure><img alt="Graph of the 95th percentile of the response time per engine" src="https://cdn-images-1.medium.com/max/1024/1*pX1rNmXuVPNNUuCfxzo88Q.png" /><figcaption>95th percentile of the response time per engine</figcaption></figure><p>The 95th percentile shows a somewhat greater difference. It is not enormous, but five milliseconds does feel significant.</p><p>Regarding response time, we can say, without any doubt, that the `JIT engine’ is not worth it, mathematically speaking. However, we are talking about a five-millisecond difference, so it is not really a big deal either.</p><p>— <strong>System metrics (CPU &amp; RAM)</strong></p><p>The service is deployed on a Kubernetes cluster. I have access to the cluster metrics and can see how much CPU and RAM are used by the service.</p><blockquote>In Kubernetes, we set CPU and RAM reservations. It is a good practice to indicate to the cluster how many resources the pods will need.<br>For example, we can define that one Node.js process can use up to two CPUs.<br>The graph then shows how much of these two CPUs are used compared to the reservation.<br>The same applies to RAM. Of course, the graph shows values for the entire cluster, not just one pod.<br>```yaml<br>limits:<br> memory: 512Mi<br>requests:<br> memory: 512Mi<br> cpu: 2<br>```<br>This is an example of a configuration for a pod. This means the pod will have two CPUs and 512Mi of RAM at its disposal.</blockquote><figure><img alt="Percent of the CPU reservation used" src="https://cdn-images-1.medium.com/max/1024/1*QAb-zvfH7o_TgSxxQZvijQ.png" /><figcaption>Percent of the CPU reservation used</figcaption></figure><p>The `js-engine’ uses 2% less CPU than the `JIT-engine.’ It is a consistent difference, but like the response time gain, it is not that significant. Don’t get me wrong, when you deploy hundreds or thousands of pods, 2% can mean a lot, but I’m not convinced it does in my case.</p><figure><img alt="Percent of the RAM reservation used" src="https://cdn-images-1.medium.com/max/1024/1*8cqisp9nQLJYnCrdB__xow.png" /><figcaption>Percent of the RAM reservation used</figcaption></figure><p>The RAM usage is a bit more interesting. The `js-engine’ uses ~20% less RAM than the `JIT-engine.’ Here, a 20% difference feels substantial.</p><p>—<strong> Node.js metrics</strong></p><figure><img alt="Event Loop Utilization min at cluster level, max and average" src="https://cdn-images-1.medium.com/max/1024/1*U0oInN-5wllXrWDLMUl2dg.png" /><figcaption>Event Loop Utilization min at cluster level, max and average</figcaption></figure><p>I challenge you to identify when the deployment occurred. The ELU (Event Loop Utilization) did not shift a single bit, even with 50% of the requests being handled by the `js-engine.’ This graph only shows the overall cluster level, as I cannot get metrics for each engine separately. In my opinion, since 50% of the pods use another engine, if the ELU were significantly impacted, we would see it. I will assume that using either `JIT’ or `js’ does not change anything in terms of Event Loop Utilization.</p><figure><img alt="Heap old space usage at cluster level, min, max, and average" src="https://cdn-images-1.medium.com/max/1024/1*UrCTdYvMyzk02lfrDbWwow.png" /><figcaption>HEAP old space usage at cluster level, min, max, and average</figcaption></figure><p>Monitoring the HEAP shows that the `old’ space seems to have reduced slightly. It is not very obvious in the graph but it is visible in the average. The maximum did not change, but the minimum did. This confirms what we observed earlier with RAM usage.</p><p>Other HEAP spaces were not impacted at all, and the same goes for Garbage Collection. They all tell the same story as the ELU metrics. They remain unchanged from before, so I didn’t bother screenshotting the graphs.</p><h4>Lab results</h4><p>I ran the Gatling test for each engine in two separate deployments. The tests were run simultaneously. Strictly speaking, they could have impacted one another, but I don’t think this effect is significant, as the cluster I was using had enough resources to handle the load. Moreover, the load was not that high, and I kept the request rate well below limits.</p><p>—<strong> Response times</strong></p><figure><img alt="Average and 95th percentile response time per engine" src="https://cdn-images-1.medium.com/max/1024/1*VGaqGRwkzWfQ85g2imToJA.png" /><figcaption>Average and 95th percentile response time per engine</figcaption></figure><p>The results are not favorable for the `js` engine. The difference is substantial. We must remember that this is a lab environment, and response times may differ from those in production. However, we are losing about 50 milliseconds on the 95th percentile and 20 milliseconds on average. These represent slowdowns of approximately 50% and 70%, respectively, compared to the `JIT’ engine.</p><p>— <strong>System metrics (CPU &amp; RAM)</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EVX7pI6qhXcmYzSXy3zRPA.png" /><figcaption>Dashboard of System metrics showing CPU and RAM for the js engine</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BHLuX2vEBFn0b_xbAsQKgw.png" /><figcaption>Dashboard of System metrics showing CPU and RAM for the JIT engine</figcaption></figure><p>The first notable difference is in CPU usage. The `js-engine’ uses 20% more CPU than the `JIT-engine,’ which is significant.</p><p>Regarding RAM, the difference is smaller but still present, with only a few percentage points of increased usage for the `js-engine.’</p><p>Overall, our synthetic tests indicate that the JIT engine is more efficient than the JS engine.</p><p>—<strong> Node.js metrics</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HiJ2wa3_jfoajWlArXWj0A.png" /><figcaption>Dashboard of Node.js metrics such as ELU, HEAP, and Garbage Collector for the js engine</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BHLuX2vEBFn0b_xbAsQKgw.png" /><figcaption>Dashboard of Node.js metrics such as ELU, HEAP, and Garbage Collector for the JIT engine</figcaption></figure><p>The story remains consistent with the Node.js metrics. The `js engine’ uses more HEAP, more Garbage Collection, and has a higher Event Loop Utilization (ELU). Specifically, the ELU increased from 30% to 50%.</p><h3>What is the meaning of all of this?</h3><p>Okay, things are getting interesting. The production results clearly show a slight advantage for the `js’ engine. This win is small and could, in some cases, be considered negligible. However, it is still a win. On the other hand, the story is entirely different when we look at the lab results. The `JIT’ engine is a clear and substantial winner! Moreover, based solely on the lab results, it is a no-brainer to use the `JIT’ engine.</p><p>The lab results were essentially the same as those I had years ago. The `JIT’ engine is faster, more efficient, and requires fewer resources in a lab environment. Back then, I only had those results to inform my decision and naturally chose the `JIT’ engine.</p><p>Today, however, the production results complicate the decision:</p><ul><li>The `js’ engine is better or at least as good as the `JIT’ engine in terms of response time, resource usage, and overall performance.</li><li>The complexity introduced by the `JIT’ engine has a cost.</li><li>The `JIT’ engine has certain limitations. We did not encounter these, so they may not be highly relevant in my context.</li><li>The `js’ engine is effectively the “standard.”</li></ul><p>The question has been raised internally, and there seems to be a consensus that standardization is important enough to justify switching back to the `js’ engine. I tend to agree with this perspective.</p><h3>Why does the optimization not show up in production?</h3><p>The production and lab tests have opposite behaviors that feel counterintuitive. I believe the main reason lies in how we utilize the GraphQL API.</p><p>When we began our GraphQL journey, we had a steep learning curve. One of the biggest challenges was designing the schema and envisioning the queries that would be made. We had to rethink everything, moving away from our accustomed REST APIs.</p><p>However, as anyone who has worked on an existing system knows (do I hear “legacy”?), we had to consider many existing scenarios and technical limitations. For example, paginated results were not as common back then as they are today. When designing the schema, we aimed to follow the “state of the art” in this regard, but it was not always straightforward or even feasible.</p><p>After years of learning and striving towards best practices in GraphQL, we find ourselves in a much better situation than expected. For instance, we initially feared processing massive queries with numerous nested fields. This concern hasn’t completely disappeared, but looking at the queries generated by our apps, the reality isn’t as daunting as we imagined.</p><p>Teams have adapted to the new way of thinking, embracing the limitations and concepts of a GraphQL API. Gradually, they’ve accepted the need to approach things differently. The quality of our GraphQL queries seems sufficient, indicating that we no longer require the `JIT’ optimizations.</p><p>One of the best use cases for `JIT’ appears to be when dealing with complex queries that have many nested fields. We currently don’t have that issue, or at least not to a significant extent.</p><p>In the future, we may need to reassess this decision based on how we develop our upcoming applications and frontends. But for now, the `js’ engine seems more than adequate.</p><h3>Conclusion</h3><p>A few years ago, I designed a GraphQL server and made several architectural decisions. We implemented some obvious optimizations and conducted tests to verify their effectiveness beyond marketing claims, confirming our choices.</p><p>Not all decisions were perfect, and there is much to discuss and critique about the project. However, the choice of the ‘JIT’ engine was a good one at the time.</p><p>Today, the context has evolved. We have (enough) real-world clients, and both Node.js and V8 have been improved. Additionally, the standard implementations of GraphQL have benefited from years of community enhancements.</p><p>Revisiting old decisions has proven to be insightful, if not beneficial. The ability to test a theory in production easily and without the fear of breaking anything is a luxury. I’m grateful for this opportunity.</p><p>Ultimately, we can conclude that the real world always surpasses the lab and its benchmarks. While this may not come as a surprise, it’s always a good reminder. If you have the resources and time, take the opportunity to test your hypotheses using real-world data or actual clients whenever possible. For this, it’s crucial to maintain healthy development practices that facilitate easy and safe testing and deployment.</p><p>In conclusion:</p><blockquote>The ‘JIT’ engine seems to be worthwhile. However, I would recommend conducting proper tests, if feasible, before fully committing to it. Gains may be small or even imperceptible, depending on your circumstances.</blockquote><blockquote>Anyway, always monitor, metrics are essential!</blockquote><p>— <br>Many thanks to <a href="https://www.linkedin.com/in/pauline-m-b8703048/">Pauline Munier</a> and <a href="http://www.linkedin.com/in/gillian-kelly)">Gillian Kelly</a> for their help in writing this article.<br>You can also find <a href="https://blog.ztec.fr/en/2025/post/is-graphql-jit-worth-it/">this article</a>, <a href="https://blog.ztec.fr/2025/post/graphql-jit-est-t-il-vraiment-plus-performant/">a French version of it</a>, and more on my personal blog <a href="https://blog.ztec.fr/en/">https://blog.ztec.fr/en/</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MpC_v1okzVD168X_axj-lQ.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=64e66f21dbb8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/graphql-jit-is-it-worth-it-64e66f21dbb8">GraphQL JIT, is it worth it?</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spotlight on Forum PHP 2024]]></title>
            <link>https://medium.com/deezer-engineering/spotlight-on-forum-php-2024-048eb06ae3db?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/048eb06ae3db</guid>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[forum-php]]></category>
            <category><![CDATA[afup]]></category>
            <category><![CDATA[conference]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Sébastien COURJEAN]]></dc:creator>
            <pubDate>Thu, 28 Nov 2024 10:08:50 GMT</pubDate>
            <atom:updated>2024-11-28T10:08:50.090Z</atom:updated>
            <content:encoded><![CDATA[<p><em>co-authored by Cédric Vincent, Florian Minin, </em><a href="https://twitter.com/nH_Zekia"><em>Anthony Legros</em></a>, Adriana Vizinho, <a href="https://twitter.com/Ztec6"><em>Loic Doubinine</em></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*7Eo-SNMiVs0HpIuE" /></figure><h3>What is Forum PHP?</h3><p>On October 10th-11th, 2024, several team members traveled to Disneyland Paris’ Art of Marvel Hotel for Forum PHP 2024, organized by the AFUP (Association française des utilisateurs de PHP or the French Association of PHP Users). We were looking forward to taking part in the event as a Silver sponsor, with a stand in the hall.</p><p>If you’re not familiar with the annual conference, it’s the largest gathering of the French-speaking PHP community. Over the course of two days, more than 650 people were able to attend around thirty conferences divided into three parallel tracks, participate in a community evening, sponsor demos and test our now famous music quizzes.</p><p>Thanks to Deezer, twelve of us were able to attend the conference and man the Deezer stand.</p><h3>Our favorite conferences</h3><p>We won’t summarize the thirty or so talks here, as they will be available to view in December on the <a href="https://www.youtube.com/@afupPHP">AFUP Youtube channel</a> like an advent calendar. Instead, we’ve put together a small selection of our favorite topics.</p><h4>Day 1</h4><p>How can we not mention the two talks presented by our Deezer colleagues? Right after the opening keynote, Adriana talked about management with a very real comparison between an orchestra and a tech team. On the same day, during the afternoon session, Vincent gave a talk about setting up tracing in an application using the OpenTelemetry protocol.</p><p><strong>“L’équipe technique vue comme un orchestre : l’individu au service du groupe”<br></strong>by Adriana VIZINHO, Head of Engineering at Deezer</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*xCXcbRvkCsj9AKan" /></figure><p>Adriana began her talk by drawing parallels between the different positions in an orchestra and those in a tech team. This was followed by an overview of the workings and objectives of an orchestra, with real-life cases such as a malfunction during a performance, as well as personal feedback. All this was done to highlight the importance of team cohesion, initiative-taking and common objectives that all members of an orchestra share. In conclusion, it was a good overview of the management issues that a tech team may encounter and the ideal vision of a manager’s role that everyone hopes for.</p><p>Ask yourself the following questions about your technical team:</p><ul><li>Is there team cohesion?</li><li>Do you have the freedom to take initiative?</li></ul><p><strong>“Les nouveautés dans Symfony 7.x”<br></strong>by Nicolas GREKAS at SYMFONY SAS</p><p>What is new with Symfony?</p><p>First of all, Nicolas reminded us of best practices to update the framework easily and confirmed the release date of version 7.2 at the end of November. He invited us to update bug fixes every month, minor versions every six months, and use continuous migration to have a major version every two years. In his presentation, he showed some new features (html sanitizer, clock, remote event, etc.), bridges (mailer, sweego, postal, etc.) and news on dependency injection (long list of new attributes).</p><p><strong>“PHP/Parallel : Accélérer sensiblement ses temps d’exécution”<br></strong>by Thierry Kauffmann, Linux systems administrator at Arawa”</p><p>PHP/Parallel is a PHP extension that allows you to perform multithreading.</p><p>The speech was a ROTI of a specific project where, thanks to this extension, Thierry, a Linux systems administrator at Arawa, managed to handle load balancing of video conference servers for the government (he was able to reach 100 SSH connections in two seconds instead of three minutes in sequential).</p><p>Thierry started with some definitions to distinguish the main concepts of multithreading. We won’t go into details but what we need to understand here is that PHP isn’t able to handle multithreading by default because of its engine, but thanks to the Parallel extension, it’s possible to launch multiple threads at the same time.</p><p>The Parallel extension, which is <a href="https://github.com/krakjoe/parallel">available on GitHub</a>, relies on the following principle: “Do not communicate by sharing memory; instead, share memory by communicating.” It allows you to handle new objects to implement parallel execution.</p><p>Basic examples were presented to highlight the usage of the library. Then, at the end of the presentation, Thierry discussed the limitations of this extension:</p><ul><li>Yield cannot be used;</li><li>Class or named functions cannot be declared.</li></ul><p>Even though references cannot be used (which limits the use of objects), the issue can be resolved using serialization.</p><p><strong>“De “0” à “tracing distribué” en quelques lignes de code, c’est possible !” </strong><br>by Vincent LEPOT, Senior Expert Back End SRE at Deezer</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OQyaPbhXbLXTyy_W" /></figure><p>We started the afternoon with a presentation on application tracing by our colleague Vincent.</p><p>Starting with a simple application that manages users’ favorite films, he showed us how to set up distributed tracing on a PHP application in just a few steps, using the OpenTelemetry protocol:</p><ol><li>Choose a backend storage — Vincent chose this OpenSource solution <a href="https://github.com/grafana/docker-otel-lgtm">https://github.com/grafana/docker-otel-lgtm</a></li><li>Install PHP extension opentelemetry, PHP&gt;=8.0</li><li>Install the Auto-instrumentation package, see <a href="https://packagist.org/search/?query=open-telemetry&amp;tags=instrumentation">https://packagist.org/search/?query=open-telemetry&amp;tags=instrumentation</a></li><li>Configure OpenTelemetry in your application</li></ol><p>Once the tracing is in place and using a viewer such as Tempo Grafana, the data collected can be used to monitor error problems in your application, as well as highlight latencies due, for example, to the multiplication of SQL queries (N+1) or multiple synchronous API calls.</p><p><strong>“How to debug bizarre PHP bugs”</strong><br>by Kévin DUNGLAS, Creator of FrankenPHP and Co-founder of “Les Tilleuls.coop”</p><p>This presentation was a personal return of experience on a complex bug that was related to the Xdebug library itself.</p><p>Kévin Dunglas proposed an approach that allows us to identify the root cause of a bug. First, he exposed a decision tree from two possible points of view:</p><ul><li>From the user of a library who faces a bug</li><li>From the maintainer of the library who sees all the bug requests in his repository</li></ul><p>If this wasn’t enough to find the source of the problem, he listed the several steps to follow to find it:</p><ol><li>Reproduce the bug: remove as many layers as possible to remove all potential external sources of errors</li><li>Gather information</li><li>Read the documentation</li><li>Seek theories</li><li>Write and submit a patch</li></ol><p>Kévin also explained how to submit clean bug reports to the maintainer.</p><p>You can find his slides <a href="https://dunglas.dev/2024/10/how-to-debug-xdebug-or-any-other-weird-bug-in-php/">here</a>.</p><h4>Day 2</h4><p><strong>“Une Monade, simplement, c’est quoi ?”<br></strong>by Gina BANYARD, Core Developer at PHP Foundation</p><p>During this presentation, Gina, core developer at PHP Foundation, first tried to demystify the mathematical term “monad.” Then, she went on to explain the way monads are actually used in PHP programming, with a focus on error management. She showed us the main types of monads like ‘maybe,’ ‘either’ and ‘exception.’ The talk offered an approach to functional programming that differs from traditional object-oriented programming. The way monads work reminded us of the Pipeline design pattern.</p><p>Gina’s slides can be found <a href="https://gpb.moe/doc/slides/monades-simplement.pdf">here</a>.</p><p><strong>“Et si on repensait les ORMs ?”<br></strong>by Baptiste Langlade, Software architect</p><p>Baptiste, a software architect, started by talking about Domain Driven Design using the example of a car and the registration card of the vehicle to introduce his talk about ORMs. After, he explained Formal ORM and compared it with Doctrine ORM. In short, Formal can be used by storage DB (mysql, postgres, mariadb), Filesystem (concrete, S3, memory), ElasticSearch and the ecosystem Inmind. He pointed out that he gained a lot from using the Formal system instead of the Doctrine one (Formal is 40% faster than Doctrine).</p><p>Formal is an ORM that he created himself and continues to maintain. He uses it for different cases:</p><ul><li>Generation of files</li><li>Http request / response</li><li>Input process</li><li>Asynchronous behaviour</li><li>Sending messages, etc.</li></ul><p><strong>“Optimiser les performances de votre API avec AutoMapper”<br></strong>by Joël WURTZ, Web Developer at JoliCode</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/996/0*7obqvXlfyQryBMSs" /><figcaption>@source JoliCode</figcaption></figure><p>AutoMapper is an open source PHP library created by JoliCode, which enables you to map one schema (<em>object</em> or <em>array</em>) to another. Compared to Symfony’s Serializer, it generates code instead of relying on metadata, which significantly improves performance.</p><p>The AutoMapper tries to map your properties automatically, but if you have custom normalizers, you will need to migrate them to transformers.</p><p>Joël, web developer at JoliCode, recommends using DTOs, with 1 endpoint = 1 DTO, for two reasons:</p><ul><li>Strong typing and self-documented properties;</li><li>More readable code: no need for serialisation groups.</li></ul><p>You can find his slides <a href="https://jolicode.github.io/les-confs-de-joel/optimiser-les-performances-de-votre-api-avec-automapper/dist/index.html?slideIndex=0&amp;stepIndex=0">here</a>.</p><p><strong>“The Business of Bisecting”<br></strong>by Pauline Vos, freelance software engineer and consultant</p><p>Pauline Vos, freelance software engineer and consultant, gave a talk on Git, with a focus on the git bisect command, which is very useful for identifying the commits responsible for a regression/bug.</p><p>She started with a simple demo: when facing a regression on a website, rather than manually searching through the git history, <em>git bisect</em> allows you to target the offending commit by starting with a stable commit and successively testing intermediate commits, indicating whether they are good or bad, until you isolate the one causing the problem. Git bisect can also be used to identify the cause of an abnormally long build or tests that slow down CI.</p><p>Pauline also suggested some tips such as replacing default terms “good”/”bad” with more appropriate terms, correcting errors via git bisect replay.</p><p>Finally, she ended her conference by pointing out the importance of maintaining atomic commits.</p><p><strong>“Écrire du Php avec seulement 6 caractères”<br></strong>by Benoît Viguier, Senior Developer at Lendable</p><p>Benoît, senior developer at Lendable, gave a relaxed talk where he presented how he accomplished a rather unconventional challenge: writing PHP code with the fewest possible characters.</p><p>He demonstrated how his library can transform any PHP code into a much shorter version. During this fun presentation, he explained the inner workings of this magic trick, step by step.</p><p>To give you a preview, here’s what a “Hello world” in 12 characters would look like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/726/0*l6cpv87Fr7pQvKxO" /></figure><p><strong>“Utiliser un framework pour écrire de la documentation ?”<br></strong>by Martin SUPIOT, Staff Engineer at Alma</p><p>One could argue that writing documentation has as many approaches as there are writers. In this context, how can we help developers write documents more easily and effectively? How can we prevent the dreaded “blank page” syndrome?</p><p>First, Martin, staff engineer at Alma, provided a theoretical framework for documentation. He then explained how, through a set of best practices, tips and a framework (Diataxis), we can enhance the developers’ experience with this task.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/813/0*gOu8vJSUJt1cfTNA" /></figure><h3>Beyond conferences</h3><h4>On the Deezer stand</h4><p>Once again, many of us turned out for Forum PHP. It was an opportunity for us to answer questions from visitors and sponsors about tech solutions at Deezer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vd-54QFXiqE8xEQM" /></figure><h4>Music quiz sessions</h4><p>To animate our stand, we chose to organize two Music Quiz sessions in “Party mode” each day during breaks. Many participants came to test their musical knowledge on the four themes we picked. Despite the background music in the hall, contestants had a lot of fun and won some great Deezer goodies.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*8gRlxPLxTQWWoLfG" /></figure><h4>Shaker</h4><p>At lunch break and during discussions with visitors, we chose to explain the recently released Shaker feature.</p><p>Shaker is a collaborative soundtrack for your future parties, road trips or family holidays. It allows you to:</p><ul><li>Create the perfect mix with your friends’ favorites</li><li>Co-create the perfect playlist for your next get-together</li><li>Discover your compatibility rates</li></ul><p>Shaker works with all platforms, and everyone is welcome, even non-Deezer users.</p><p>Learn more and join the community <a href="https://www.deezer.com/explore/fr/features/shaker/">here</a>.</p><h3>In a nutshell</h3><p>These were two incredible days in the company of the PHP community. Many thanks to the organizers, speakers, sponsors and to Deezer for allowing us to return each year.</p><p>We’re now focusing on the 2025 edition, which will celebrate 30 years of PHP and 25 years of AFUP. See you there?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=048eb06ae3db" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/spotlight-on-forum-php-2024-048eb06ae3db">Spotlight on Forum PHP 2024</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Node.js 20 upgrade: a journey through unexpected HEAP issues with Kubernetes]]></title>
            <link>https://medium.com/deezer-engineering/node-js-20-upgrade-a-journey-through-unexpected-heap-issues-with-kubernetes-27ae3d325646?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/27ae3d325646</guid>
            <category><![CDATA[kubernetes-resource]]></category>
            <category><![CDATA[troubleshooting]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[deezer]]></category>
            <dc:creator><![CDATA[Ztec]]></dc:creator>
            <pubDate>Wed, 18 Sep 2024 08:01:49 GMT</pubDate>
            <atom:updated>2024-10-24T15:20:27.434Z</atom:updated>
            <content:encoded><![CDATA[<p>This summer, I upgraded a project from Node 18 to Node.js 20. The code was not that old, and I did not expect any problems... But as with every Node.js upgrade, unexpected behaviors happened.</p><h3><strong>Why upgrade?</strong></h3><p>It is common practice to keep projects up to date. Lagging behind too much may increase the work needed to upgrade when the choice isn’t yours anymore.</p><p>I could have waited, but I had some time to do it.</p><p>Node.js 18 is currently in “MAINTENANCE”, which means there are security fixes but no more new features. Maintenance is set to end in June of next year.</p><p>Node.js 20 is the current LTS version available. It is in active development and will enter its maintenance phase next year.</p><figure><img alt="All Node.js versions from 16 to 24 and their different phases: Current, Active, Maintenance" src="https://cdn-images-1.medium.com/max/960/0*Z19F7tOs68oY82cH" /><figcaption>Node.js release roadmap</figcaption></figure><p>Soon after the next LTS (Node.js 22) is released, a migration will be scheduled (and maybe another post?)</p><h3><strong>How?</strong></h3><p>This part is pretty simple: change a few versions values in the `package.json` file, then run “npm install” as usual.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6DfEZTKXS3BTL2a1" /></figure><p>All dependencies versions are managed by <a href="https://github.com/renovatebot/renovate">renovate</a> bot. Therefore, “package.json” only contains the exact versions of each dependency.</p><p>The version is set to Node “20.15.1” because it was the very last version available at the time of the upgrade. Any future minor upgrade will be done automatically without changing anything in the “package.json” file.</p><p>Docker images are built regularly and target the latest version of Node.js 20 like a rolling release.</p><figure><img alt="Diff of the Dockerfile changing the `FROM` from one node-18 image to node-20, both maintained internally" src="https://cdn-images-1.medium.com/max/898/0*XLBrNmaVEM2E-50q" /><figcaption>Diff of the Dockerfile</figcaption></figure><h3><strong>Deployment and first results</strong></h3><p>This is a critical project, but deploying it is usually simple and quick. No one fears its deployment. But as this was a potentially impactful upgrade, I paid more attention than usual.</p><h4><strong>Project vitals</strong></h4><p>The main dashboard I look at during a deployment is the project’s vitals. It contains all the required metrics to know at a glance if the project is healthy or not. I won’t go into details here for confidentiality reasons, but I can show you the response time goal of the project:</p><figure><img alt="Graph showing the percentage of requests with a response time under 100ms, 50ms, 10ms, respectively around 97%, 90%, and 45%" src="https://cdn-images-1.medium.com/max/1024/0*UGwEU7jTWqBcSVji" /><figcaption>Response time goals</figcaption></figure><p>The purple vertical line is roughly when the deployment occurred. You can see the response time went up. Note: Given that the purple line is not displayed on all graphs, follow the red arrow instead. The graph shows the percentage of responses that match one of the three response time goals we follow. Requests faster than 100ms went from 96.8% to 96.1% — a minor defeat at the first battle.</p><p>Sure, we can agree that losing around 1% is perfectly acceptable, but it shows that something happened.</p><p>Looking at the average response time, we can see the increase more easily. It went from ~23ms to ~28ms.</p><figure><img alt="Graph showing the average response time oscillating between 22ms and 25ms" src="https://cdn-images-1.medium.com/max/1024/0*VeHg7YyjjOo-duhV" /><figcaption>Response time average</figcaption></figure><p>I then looked at all the other graphs, metrics, and logs. After validating and considering the project as healthy, it was time to dig in to understand what was happening. As there was no immediate threat to the project’s stability and the response time was still well above the <a href="https://www.atlassian.com/incident-management/kpis/error-budget">budget</a> thresholds, I could take some time to dig deeper; otherwise, a rollback would have been in order.</p><h4><strong>System vitals</strong></h4><p>The project is running on a Kubernetes cluster. I have access to the cluster’s metrics and can see the pods’ CPU and RAM usage. Those unfamiliar with Kubernetes can consider a pods like one Node.js process started inside a Docker container.</p><p><strong>CPU increase</strong><br>I noticed an increase in CPU usage, going from 24% to 30% of the reservation after the deployment — another defeat.</p><figure><img alt="The CPU went from 24% to 30%" src="https://cdn-images-1.medium.com/max/849/0*v6BF1ljEaBSsN-q-" /><figcaption>CPU Graph</figcaption></figure><blockquote>In Kubernetes, we set CPU and RAM reservations. It is a good practice to indicate to the cluster how many resources the pods will need.<br>For example, we can define that one Node.js process can use up to 2 CPUs.<br>The graph then shows how much of these two CPUs is used compared to the reservation.<br>The same applies to RAM. Of course, the graph shows values for the entire cluster, not only one pod.</blockquote><pre>limits:<br>  memory: 512Mi<br>requests:<br>  memory: 512Mi<br>  cpu: 2</pre><blockquote>This is an example of a configuration for a pod. This means the pod will have two CPUs and 512Mi of RAM at its disposal.</blockquote><p><strong>Memory usage decrease</strong><br>The memory usage went down. A small victory!</p><figure><img alt="The RAM went from 75% to 55%" src="https://cdn-images-1.medium.com/max/1024/0*Uah1tiLOcriJOICG" /><figcaption>RAM usage</figcaption></figure><p>This project always loses weight after each deployment. It is normal, and it gains it back after a while. It takes a few hours to stabilize, but here, it went down more than usual, and at first glance, it will likely stay like that.</p><h4><strong>Node.js vitals</strong></h4><p>The first Node.js metric I was interested in is the <a href="https://nodesource.com/blog/event-loop-utilization-nodejs/">Event Loop Utilization (ELU)</a>.</p><p>This metric is essential to understand the health of a Node.js process. It basically shows how much time the process is spending doing work in the <a href="https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick">event loop</a>, from ~0% for an idle process to 100% for a process that is always executing JavaScript code.</p><figure><img alt="The ELU went from 17% to 20% on average" src="https://cdn-images-1.medium.com/max/1024/0*E8Gz8ybLoF811vp6" /><figcaption>Event Loop Utilization</figcaption></figure><p>The graph shows a slight increase after the deployment, going from 17% to 20% on average. A defeat, again! It is not really surprising as we know the CPU usage increased, but we can clearly see that it had a (small) impact on the JavaScript code running. This may be the reason for the increase in response time.</p><p>After that, I looked at the HEAP and the Garbage collector statistics. They both make sense together.</p><figure><img alt="Multiple graphs showing all HEAP spaces and the Garbage Collector statistics" src="https://cdn-images-1.medium.com/max/1024/0*7ipYjzLQTiobCIkT" /><figcaption>HEAP and Garbage collector metrics</figcaption></figure><p>We lost weight on the HEAP, but something is happening with the Garbage Collector. The minor GC is running more often and is taking more time.</p><p>Without going into too much detail, the <a href="https://v8.dev/blog/trash-talk#minor-gc">minor GC</a> is a process that targets newly created objects in the HEAP. Another one (the Major GC) targets all “old” objects.</p><p>The Minor GC is a fast process that runs often. It is normal to see it called at high volume. However, something changed after the upgrade as we went from a few calls to thousands of them. Even if it is a fast process, it ate up almost 4 seconds of CPU time on the entire cluster. By comparison, we were only eating up less than 1 second before the deployment.</p><p>This increase in GC can easily explain the CPU usage and part of the ELU increase, which results in a slower response time.</p><p>I might have my culprit here.</p><h3><strong>Issue identified, now what?</strong></h3><p>Ok, there is clearly something going on with the Garbage Collector. But what is it?</p><p>If we look at the HEAP in detail, there were a few notable changes after the deployment:</p><figure><img alt="Gaph of few HEAP spaces size: map, new, shared" src="https://cdn-images-1.medium.com/max/575/0*qJ-1QQ2b5clz9ect" /><figcaption>Gaph of few HEAP spaces size: map, new, shared</figcaption></figure><p>The <strong>map</strong> HEAP space disappeared, and the <strong>shared</strong> spaces appeared. But most importantly, the <strong>new </strong>space went from ~33MB to ~8MB.</p><p>Do you remember what I wrote a few lines back?</p><blockquote>The Minor GC targets newly created objects in the HEAP</blockquote><p>Is this the reason why the GC is now running so often? I think a smaller space means more frequent GC runs because when the space is full, the GC needs to run to free some space. If the space is smaller, it will become full quicker and run more often.</p><h4><strong>Why is the new space smaller?</strong></h4><p>There is no configuration of the HEAP that targets the new space in the project. This means Node.JS itself probably changed between versions 18 and 20. But there is one HEAP space that is configured: the old space. We run the server with the following command:</p><pre>node - max-old-space-size=300 dist/server.js</pre><p>I don’t know if this impacts the new space as well. I started searching for references to the new space in the Node.js release notes. At first, I found nothing relevant, but after a while, I made the link between “new space” and “semi space.” The V8 engine under the hood of Node.js uses this wording instead of “new space.” And sure enough, I found a change in the Node.js 19 release notes: <a href="https://github.com/nodejs/node/pull/44436">https://github.com/nodejs/node/pull/44436</a></p><p>The parameter “ — max-semi-space-size” was added because it was itself added to the V8 engine. By following breadcrumbs, I finally found these changes in the V8 engine: <a href="https://chromium-review.googlesource.com/c/v8/v8/+/1631593">https://chromium-review.googlesource.com/c/v8/v8/+/1631593</a> and <a href="https://chromium-review.googlesource.com/c/v8/v8/+/4384482">https://chromium-review.googlesource.com/c/v8/v8/+/4384482</a></p><p>These changes modified the way the max-semi-space-size is computed. I’m no C expert, but by simply reading comments and some parts of the code, we can see that the new space’s size has changed. The new value is now computed from various others. Unfortunately, I could not find how it is computed exactly as my C++ is a bit rusty, and V8 is not a simple project. I can only make hypotheses at this investigation stage; one is that the new space’s size is computed in relation to the old space’s size.</p><p>The first change was introduced in V8 10.6 and is now part of Node.js 20, which uses 11.3. For reference, Node.js 18 was using V8 10.2. <br>This means that going from Node.js 18 to 20, we also went from V8 10.2 to 11.3.</p><h4><strong>A workaround?</strong></h4><p>The “new” HEAP space is now computed differently. Upon the upgrade, it went from ~32MB to ~8MB. It is most likely the reason why the GC is now eating up so much CPU time compared to before.</p><p>A new parameter was introduced to specify the “new” space’s size: “ — max-semi-space-size.”</p><p>I tried to set it to the previous value and see if it solved the issue.</p><p>I changed the command to start the server to:</p><pre>node - max-semi-space-size=16 - max-old-space-size=300 dist/server.j</pre><p>The value “16” comes from the <a href="https://github.com/nodejs/node/blob/86415e4688f466c67878d525db4ebc545492bcd7/doc/api/cli.md?plain=1#L3363">documentation itself</a>.</p><pre>- max-semi-space-size=SIZE (in megabytes) <br><br>Sets the maximum [semi-space][] size for V8&#39;s [scavenge garbage collector][] in<br>MiB (megabytes).<br>Increasing the max size of a semi-space may improve throughput for Node.js at<br>the cost of more memory consumption.<br>[…]<br>The default value is 16 MiB for 64-bit systems and 8 MiB for 32-bit systems.<br>[…]</pre><p>Let’s deploy this simple change and see if it works.</p><figure><img alt="Graph showing the percentage of requests with a response time under 100ms, 50ms, 10ms, and the average response time" src="https://cdn-images-1.medium.com/max/771/0*JCC83BpbOMBvWp5h" /><figcaption>Response time goals &amp; average response time</figcaption></figure><p>The response time has gone back to the previous values. It’s a win!</p><figure><img alt="Graph showing the CPU and RAM usage returning to normal values" src="https://cdn-images-1.medium.com/max/1024/0*7gua_dksecTxzDB6" /><figcaption>CPU &amp; RAM usage</figcaption></figure><p>The CPU usage is also back to normal, and the RAM usage is still lower than before. It’s a double win!</p><figure><img alt="Graph showing the ELU, HEAP and GC returning to normal values" src="https://cdn-images-1.medium.com/max/1024/0*LHBD0T6hDUXASH-A" /><figcaption>Node.js metrics including ELU, HEAP and GC</figcaption></figure><p>Finally, we can see that the GC went back to a “normal” behavior. Also, the new space is now back to its original value. It confirms my hypothesis that the new space’s size was the issue.</p><h3><strong>What happened?</strong></h3><p>At this point, the issue has been solved in production. But I still need to understand what exactly caused the new space to shrink like this. I hypothesize that the `max-semi-space-size` is now computed in relation to the old space’s size. Since we specify a ` — max-old-space-size` of 300MB, a relatively low value for Node.js, it would not be surprising that it significantly impacted the “new” space’s size and caused it to shrink that much.</p><p>Let’s build a lab project to test and confirm this hypothesis.</p><h4><strong>Lab project</strong></h4><p>I found a basic HEAP consumption script online and used the standard “V8” module to obtain HEAP statistics.</p><p>Here is the resulting script:</p><pre>const v8 = require(&#39;v8&#39;);<br><br>// Dumb function stolen somewhere on the internet and modified to allocate<br>// lots of new objects. A bit barbaric, I know, but it does the job.<br>function allocateMemory(size) {<br>    // Simulate allocation of bytes<br>    const numbers = size / 8;<br>    const arr = [];<br>    arr.length = numbers;<br>    for (let i = 0; i &lt; numbers; i++) {<br>        arr[i] = {&quot;test&quot;: Math.random()};<br>        arr[i][`${Math.random()}`] = Math.random();<br>    }<br>    return arr;<br>}<br>// We allocate memory to trigger the garbage collector <br>// &amp; force the HEAP to grow, including the `new_space`<br>allocateMemory(1000000)<br>const heapSpaces = v8.getHeapSpaceStatistics()<br>console.log(<br>    heapSpaces.filter(item =&gt; item.space_name === &#39;new_space&#39;)[0].space_size <br>    / 1024 / 1024 // We display the value in MB for convenience<br>);</pre><p>When running this script, the output will be a single number representing the size of the “new_space” in MB. Before outputting it, I ensured that this space was grown to its maximum value by allocating objects in memory.</p><pre>$ node test.js<br>32</pre><p>Now, let’s run this script with the “ — max-old-space-size” set to 300MB using Node 18 and then Node 20.</p><pre>$ nvm use 18<br>Now using node v18.20.4 (npm v10.7.0)<br>$ node - max-old-space-size=300 test.js<br>32<br>$ nvm use 20<br>Now using node v20.15.1 (npm v10.7.0)<br>$ node - max-old-space-size=300 test.js<br>32</pre><p>The “new_space” size is the same for both versions. I was clearly wrong.</p><p>Of course, doing the same without the “ — max-old-space-size” gives the exact same result.</p><figure><img alt="Me (represented by William Hartnell) watching the results with astonishment" src="https://cdn-images-1.medium.com/max/300/1*XW2_Co24JV-XBC1oIWkCiA.png" /><figcaption>Me watching the results</figcaption></figure><p>What is going on? Why is my project behaving differently? At this stage, I can’t understand the reason why.</p><p>After more searching in V8 code and, most importantly, a hint from one of my colleagues <a href="https://pouet.chapril.org/@zibok">Zibok</a>, I may have a new hypothesis related to the memory reservation in the Kubernetes configuration.</p><p>The hypothesis now is the “new_space” size is computed in relation to the memory reservation of the pod.</p><blockquote>Without going into details, Kubernetes memory and CPU reservation are done through the Kernel.<br>Meaning that what is actually enforcing the reservation is the Kernel itself. It is applied directly to the process running inside the pod via the <a href="https://en.wikipedia.org/wiki/Cgroups">cgroups</a>.<br>Therefore, the process itself can be aware of the limit and adapt.</blockquote><p>I could test that in a Kubernetes Cluster, but there may be an easier way to test this hypothesis. Sure enough, Docker allows setting memory reservations, too, via the “— memory parameter”. More on that <a href="https://docs.docker.com/config/containers/resource_constraints/">here</a>.</p><p>Firstly, the results without memory reservation:</p><pre>$ docker run -ti - rm -v ./:/ node:18 node /test.js<br>32<br>$ docker run -ti - rm -v ./:/ node:20 node /test.js<br>32</pre><p>These results show the exact same result as before, as expected.</p><p>Then, with limits:</p><pre>$ docker run - memory=512m -ti - rm -v ./:/ node:18 node /test.js<br>32<br>$ docker run - memory=512m -ti - rm -v ./:/ node:20 node /test.js<br>2</pre><p>Finally, we have a different result. The “new_space” size is now 2MB. It confirms the hypothesis that the memory reservation is used to compute the “new_space” size.</p><figure><img alt="Me (represented by Peter Capaldi) dancing of joy and walking away from a small tardis door" src="https://cdn-images-1.medium.com/max/220/0*6WXL3UPNGzs5zKp4" /><figcaption>Me celebrating the results</figcaption></figure><p>I now have my culprit. The memory reservation in the Kubernetes configuration is the reason why the “new_space” size shrunk so much.</p><h3><strong>Conclusion</strong></h3><p>The overall upgrade to Node.js 20 is a win. The global memory consumption is significantly lower than before, and apart from the few moments when the “new_space” size was too small, everything worked flawlessly.</p><p>When using Node.js in a Kubernetes environment, and more broadly in a containerized scenario, you must consider the memory and CPU reservation. It needs to be configured in order to set limits to your process that would otherwise consume more than you expected. Also, setting it to low values requires attention to ensure the process accommodates the limit appropriately.</p><p>Today, we saw that setting RAM limits on a process also impacts how much it will allow itself to consume. Production services with high loads or receiving tons of requests can impact performance and latency. If I had not monitored the metrics, we would have missed our target response time, dropping by a whole percent! A shame, I know.</p><p>The impacts of CPU limits on Node.js processes would make good material for a next post, as I observed surprising effects when I did some tests a few years ago. Maybe I’ll write about it in the future… So keep your eye on the blog!</p><p>In conclusion :</p><blockquote>In Node.js, HEAP space is now (since Node 19) sized in relation to the memory limits of the process (among other things).</blockquote><blockquote>In a Kubernetes/Container context, where setting RAM limits is a common practice, it can unexpectedly impact the performance of your Node.js process.</blockquote><blockquote>The “ — max-semi-space-size” parameter must be considered in those scenarios.</blockquote><p><strong>Update 24/10/2024<br></strong><a href="https://github.com/joebowbeer">Joe Bowbeer</a> opened an <a href="https://github.com/nodejs/node/issues/55487">issue</a> and submitted a <a href="https://github.com/nodejs/node/pull/55495">PR</a> on the Node.js project to clarify the semi-space behavior. <br>The PR is still opened for review as of today.</p><p>Many thanks to <a href="https://www.linkedin.com/in/pauline-m-b8703048/">Pauline Munier</a> and <a href="http://www.linkedin.com/in/gillian-kelly">Gillian Kelly</a> for their help in writing this article.</p><p>You can also find this article (and more) on my personal blog:<br><a href="https://blog.ztec.fr/en/2024/post/node.js-20-upgrade-journey-though-unexpected-heap-issues-with-kubernetes/">https://blog.ztec.fr/en/2024/post/node.js-20-upgrade-journey-though-unexpected-heap-issues-with-kubernetes/</a></p><p>Note: A French version of this article is also available on the blog: <a href="https://blog.ztec.fr/2024/post/node.js-20-aventure-inattendue-kubernetes/">https://blog.ztec.fr/2024/post/node.js-20-aventure-inattendue-kubernetes/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=27ae3d325646" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/node-js-20-upgrade-a-journey-through-unexpected-heap-issues-with-kubernetes-27ae3d325646">Node.js 20 upgrade: a journey through unexpected HEAP issues with Kubernetes</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Optimizing Kubernetes resources with Horizontal Pod Autoscaling via Custom Metrics and the…]]></title>
            <link>https://medium.com/deezer-engineering/optimizing-kubernetes-resources-with-horizontal-pod-autoscaling-via-custom-metrics-and-the-a76c1a66ff1c?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/a76c1a66ff1c</guid>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[hpa]]></category>
            <category><![CDATA[autoscaling]]></category>
            <category><![CDATA[prometheus]]></category>
            <dc:creator><![CDATA[Weeking]]></dc:creator>
            <pubDate>Wed, 28 Aug 2024 08:11:24 GMT</pubDate>
            <atom:updated>2024-08-28T08:11:24.608Z</atom:updated>
            <content:encoded><![CDATA[<h3>Optimizing Kubernetes resources with Horizontal Pod Autoscaling via Custom Metrics and the Prometheus Adapter</h3><p><em>co-authored by </em><a href="https://medium.com/u/1df55d0db6c5"><em>Denis GERMAIN</em></a></p><p>If you’ve lived in a cave for the last 10 years and have never heard of <a href="https://kubernetes.io/fr/">Kubernetes</a>, we’ll be brief and say that it’s an extensible open-source platform that automates the deployment, scaling, and management of containerized applications.</p><p>Its popularity stems from its ability to provide native scalability for your apps, portability across different environments (on-prem, cloud, hybrid), and robust features like self-healing and rolling updates. Supported by a vibrant community (and a foundation, the <a href="https://www.cncf.io/">CNCF</a>), Kubernetes has become the de facto standard for application management in a containerized ecosystem.</p><p>At Deezer, Kubernetes has been deployed and used in production since 2018 in a bare-metal environment. We also have some clusters on the public cloud.</p><p>Over the years, we (and we are not the only ones) have come to realize that scaling applications to the right size and the right numbers of replicas is hard. Hopefully, Kubernetes can help us with that.</p><p>Ready to scale? Let’s go!</p><h3>Understanding Horizontal Pod Autoscaling (HPA)</h3><p>HPA is a key feature in Kubernetes. It allows you to specify targets for some representative metrics (like CPU consumption of a Pod) to determine automatically whether Kubernetes should automatically scale up (or down) your app.</p><p>Like everything in Kubernetes, it consists of an API (currently <strong>autoscaling/v2</strong>). The easiest way to interact with it is to create a YAML manifest, where you describe what the desired state of your application is, depending on the load.</p><p>By default, only simple metrics, CPU and memory consumption (collected by <a href="https://github.com/kubernetes-sigs/metrics-server">metrics-server</a>) are available to the user to specify the scaling rules.</p><p>A simple example could look like this:</p><pre>apiVersion: autoscaling/v2<br>kind: HorizontalPodAutoscaler<br>metadata:<br>  name: myapp-hpa<br>  namespace: mynamespace<br>spec:<br>  maxReplicas: 6<br>  metrics:<br>  - resource:<br>    name: cpu<br>    target:<br>      averageUtilization: 50<br>      type: Utilization<br>    type: Resource<br>  minReplicas: 2<br>  scaleTargetRef:<br>    apiVersion: apps/v1<br>    kind: Deployment<br>    name: myapp</pre><p>Note: HPA specs can be incredibly complex and powerful as the API has been significantly enriched over the years. Please read the <a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">official documentation</a> to learn more about this.</p><p>Based on the previous example, once this manifest is applied, Kubernetes will try to maintain no more than 50% average CPU usage on all the <strong>myapp</strong> Pods, and will add replicas if the average CPU consumption goes above this threshold. As soon as the CPU goes below the target, Kubernetes will scale down.</p><p>But while HPA using CPU and memory metrics is effective for many scenarios, it has limitations:</p><ul><li>Modern applications often have complex performance characteristics that are not fully captured by CPU and memory usage alone. For example, an application might be I/O-bound. Other factors like request latency, custom business metrics, or external service dependencies might provide a better basis for scaling decisions. CPU usage may also be very high when “booting” a new Pod, which in turn might trigger more scaling than necessary.</li><li>HPA reacts to current metrics, which means there may be a delay between the consumption spike and the scaling response. This can lead to temporary performance degradation. Finding metrics that can <strong>anticipate</strong> the need to scale rather than react after a degradation can improve reliability.</li></ul><p>To address these limitations, Kubernetes allows the use of custom metrics, providing greater flexibility and control over the scaling behavior of applications. This is where tools like Prometheus and Prometheus Adapter come into play, enabling more nuanced and effective autoscaling strategies.</p><h3>Prometheus and the /-/metrics</h3><p>Another great project under the CNCF’s umbrella is <a href="https://prometheus.io/">Prometheus</a>, an open source monitoring and alerting tool. Prometheus features a Timeseries Database optimized for storing infrastructure metrics and a query language allowing easy yet powerful deep dives in those metrics.</p><p>Usually, monitoring tools fall under two major categories. Those that receive the metrics from clients who “push” them, and those that periodically “pull” the metrics from the apps themselves. Prometheus uses the pull strategy (most of the time), and by default it <em>pulls</em> the metrics every 30 seconds.</p><p>This means that you don’t need to install an “agent” on your apps, BUT that you have to specify to Prometheus a list of “targets” that expose HTTP endpoints (your apps) serving metrics in a specific format, usually on a /-/metrics path:</p><pre>$ kubectl -n mynamespace port-forward myapp-5584c5c8f8-gbsw8 3000<br>Forwarding from 127.0.0.1:3000 -&gt; 3000<br>Forwarding from [::1]:3000 -&gt; 3000<br># in another terminal<br>$ curl localhost:3000/-/metrics/ 2&gt; /dev/null | head<br># HELP http_request_duration_seconds duration histogram of http responses labeled with: status_code, method, path<br># TYPE http_request_duration_seconds histogram<br>http_request_duration_seconds_bucket{le=&quot;0.0002&quot;,status_code=&quot;200&quot;,method=&quot;GET&quot;,path=&quot;/-/health&quot;} 0<br>http_request_duration_seconds_bucket{le=&quot;0.0005&quot;,status_code=&quot;200&quot;,method=&quot;GET&quot;,path=&quot;/-/health&quot;} 364<br>[…]</pre><p>We can then query Prometheus for those metrics by specifying the metric name and adding labels to specify which subset is of interest to us:</p><pre>http_request_duration_seconds_bucket{kubernetes_namespace=’mynamespace’, kubernetes_pod_name=”myapp-5584c5c8f8-gbsw8&quot;}</pre><p>With Prometheus being the de facto standard when dealing with microservices in containerized environments, we will not give the instructions on how to deploy it here. We will assume you already have a running Prometheus server (or equivalent, as there are compatible alternatives) in your cluster, scrapping metrics from applications.</p><p>If you need a starting point, we advise you to take a look at <a href="https://prometheus-operator.dev/">Prometheus Operator</a>, which will greatly speed up the deployment of a complete Prometheus stack.</p><p>So, thanks to Prometheus, we now have a bunch of metrics to choose from in order to predict if our apps need to be proactively scaled. The problem is, however, that we can’t tell the HPA to look at these metrics directly, as the HPA is not directly compatible with Prometheus PromQL query language.</p><h3>Prometheus Adapter to the rescue</h3><p>We need another tool that will get the metrics out of Prometheus and feed them to Kubernetes. You’ll have guessed which software this is by now: <a href="https://github.com/kubernetes-sigs/prometheus-adapter">Prometheus Adapter</a>.</p><p>Installing this software is quite straightforward — you can install it from a Helm chart hosted in the repository.</p><pre>$ helm show values prometheus-community/prometheus-adapter &gt; values.yaml<br><br>$ helm install -n monitoring prometheus-adapter prometheus-community/prometheus-adapter -f values.yaml<br><br>$ kubectl -n monitoring get deployments.apps<br>NAME                            READY   UP-TO-DATE   AVAILABLE   AGE<br>metrics-server                  2/2  2         2        1d<br>prom-operator-kube-state-metrics 1/1  1         1        1d<br>prom-operator-operator          1/1  1         1        1d<br>prometheus-adapter              1/1  1         1        1d<br>thanos-prom-operator-query      3/3  3         3        1d</pre><p>In this example, you can see that we already deployed metrics server and Prometheus using Prometheus Operator, which we mentioned earlier, and that Prometheus Adapter is running.</p><p>By default, the Prometheus Adapter will be deployed with some custom metrics that we can use out of the box to scale our apps more precisely. But in this article, we will show you how to create ✨ your own ✨.</p><h3>Configuring Prometheus Adapter to Expose Custom Metrics through the API Server</h3><p>At the start, Prometheus Adapter’s configuration doesn’t contain any rules. It means that no custom metrics are exposed through the API server at first, and HPA can’t use any custom metrics.</p><p>Prometheus Adapter acts in this order:</p><ul><li>Discover metrics by contacting Prometheus,</li><li>Associate them with Kubernetes resources (namespace, Pod, etc.),</li><li>Check how it needs to expose them (if necessary, it can rename metrics),</li><li>Check how it should query Prometheus to get the actual numbers, (ex. to get a rate).</li></ul><p>First, we need to validate the custom metrics API on our cluster. Resources should be empty, but it proves the <strong><em>custom.metrics.k8s.io/v1beta1</em></strong> API is accessible.</p><pre>└─[$] kubectl get --raw &quot;/apis/custom.metrics.k8s.io/v1beta1&quot; | jq<br>{<br>  &quot;kind&quot;: &quot;APIResourceList&quot;,<br>  &quot;apiVersion&quot;: &quot;v1&quot;,<br>  &quot;groupVersion&quot;: &quot;custom.metrics.k8s.io/v1beta1&quot;,<br>  &quot;resources&quot;: []<br>}</pre><p>It then takes several steps for Prometheus Adapter to collect and provide metrics to the Kubernetes API server. The entire Prometheus Adapter configuration can be tuned through the <strong>values.yaml </strong>file of the helm release.</p><p>The first thing to configure here is “where” Prometheus Adapter can find Prometheus. If your Prometheus Adapter is running on the same cluster as your Prometheus stack, you can use a Kubernetes DNS record like below (see <a href="https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/">Kubernetes documentation DNS for Services and Pods</a>). Otherwise, you can specify an IP address (or DNS name) and a port number.</p><p>For that, edit the <strong>values.yaml </strong>with your own technical context:</p><pre>[...]<br><br>prometheus:<br>  url: http://thanos-prom-operator-query.monitoring.svc<br>  port: 9090<br>  path: &quot;&quot;<br><br>[...]</pre><p>In order to verify that Prometheus Adapter can correctly contact Prometheus, you simply need to check the Pod logs (using <strong><em>kubectl logs pod/prometheus-adapter-abcdefgh-ijklm</em></strong> or any other means at your disposal to read Pod logs).</p><p>Once this part is working, we need to add some rules to our Prometheus Adapter. In this example, we are going to use a metric called ELU (for “Event Loop Utilization”) collected from a Node.js server. It is a measure of how much time the Node.js event loop is busy processing events, compared to being idle, and it is more representative of the server load than the mere CPU percentage. You can find more information on this metric <a href="https://nodesource.com/blog/event-loop-utilization-nodejs/">here</a>.</p><p>Rules let us specify <strong>what</strong> to query in Prometheus. We can define which labels to import, and override the labels if necessary to match Kubernetes resource names. Here are the most useful values to specify:</p><ul><li><strong>seriesQuery</strong>: run the PromQL query, optionally filtered</li><li><strong>resources</strong>: associate the time series labels and Kubernetes resources</li><li><strong>name</strong>: expose time series with different names than the original ones</li><li><strong>metricsQuery</strong>: method to request Prometheus to get a rate (&lt;&lt;.GroupBy&gt;&gt; means “group by Pod” by default)</li></ul><pre>[...]<br><br>rules:<br>  default: false<br>  custom:<br>    - seriesQuery: &#39;deezer_elu_utilization{kubernetes_namespace!=&quot;&quot;,kubernetes_pod_name!=&quot;&quot;}&#39;<br>      resources:<br>        overrides:<br>          kubernetes_namespace: {resource: &quot;namespace&quot;}<br>          kubernetes_pod_name: {resource: &quot;pod&quot;}<br>      name:<br>        matches: ^deezer_elu_utilization$<br>        as: &quot;&quot;<br>      metricsQuery: sum(&lt;&lt;.Series&gt;&gt;{&lt;&lt;.LabelMatchers&gt;&gt;}) by (&lt;&lt;.GroupBy&gt;&gt;)<br><br>prometheus:<br>  url: http://thanos-prom-operator-query.monitoring.svc<br>  port: 9090<br>  path: &quot;&quot;<br><br>[...]</pre><p>You should now be able to get some custom metrics with real values by reaching the API server. To test, we will use <strong><em>kubectl</em></strong> and the <strong><em>— raw</em></strong> parameter, which gives us more power on the requests we send to the API server.</p><p>Here are some examples of commands you can run to manually check that the metrics are correctly exposed through the API server:</p><pre># list custom metrics discovery<br>kubectl get --raw &quot;/apis/custom.metrics.k8s.io/v1beta1&quot; | jq <br><br># list custom metrics values for each pod<br>kubectl get --raw &quot;/apis/custom.metrics.k8s.io/v1beta1/namespaces/&lt;namespace_name&gt;/pods/*/deezer_elu_utilization&quot; | jq </pre><p><strong>Warning</strong>: scaling will be strongly dependent on your <strong>metricScraping</strong> interval and your Prometheus Adapter discovery interval. The <a href="https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/walkthrough.md#troubleshooting-missing-metrics">official documentation </a>points out that you may run into trouble if you don’t watch out.</p><p>&gt; “<em>You’ll need to also make sure your metrics relist interval is at least your Prometheus scrape interval. If it’s less than that, you’ll see metrics periodically appear and disappear from the adapter</em>.”</p><h3>How is it going to work?</h3><p>So far in this post, we have introduced a bunch of components that interact with each other.</p><p>But how is it all going to work under the hood? Well, there’s nothing better than a diagram to explain things like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/921/0*EzQy60lIIZtf40Di" /></figure><ol><li>Prometheus scrapes the metrics exposed by our app</li><li>Prometheus Adapter queries the Prometheus server to collect the specific metrics we defined in its configuration</li><li>HorizontalPodAutoscaler (controller) will query the api-server to periodically check if the ELU metric is within acceptable limits…</li><li>…which will, in turn, ask Prometheus Adapter</li></ol><p>Let’s now create our first HorizontalPodAutoscaler!</p><h3>Using a HorizontalPodAutoscaler resource with custom metrics</h3><p>At the beginning of this post, we introduced <strong><em>HorizontalPodAutoscaler</em></strong>. The resource itself isn’t difficult to use, but we will dive a little deeper here.</p><p>Basically, HPA takes a target deployment to scale, a minimum replica number, a maximum replica number and the metrics to use. For the metrics part, we are now going to use our article’s custom metric, configured with Prometheus Adapter:</p><pre>apiVersion: autoscaling/v2<br>kind: HorizontalPodAutoscaler<br>metadata:<br>spec:<br>  maxReplicas: 6<br>  metrics:<br>  - pods:<br>      metric:<br>        name: deezer_elu_utilization<br>      target:<br>        averageValue: 500m<br>        type: Utilization<br>    type: Pods<br>  minReplicas: 2<br>  scaleTargetRef:<br>    apiVersion: apps/v1<br>    kind: Deployment<br>    name: &lt;deployment.apps_name&gt;</pre><p>When configuring an HPA resource, you only set the metric name. But how can the HPA determine the right metric from the right Pods, since several apps may expose this very same metric? To understand this, we can examine the Prometheus Adapter logs.</p><pre>I0618 12:05:29.149095       1 httplog.go:132] &quot;HTTP&quot; verb=&quot;GET&quot; URI=&quot;/apis/custom.metrics.k8s.io/v1beta1/namespaces/cmfront/pods/%2A/deezer_elu_utilization?labelSelector=app%3Dstandalone-webviews%2Crelease%3Dstandalone-webviews&quot;</pre><p>As you can see, labelSelector is added to the query. And as you mention the Deployment in the HPA <em>scaleTargetRef</em>, HPA uses the labelSelector value from the Deployment label selector. It allows you to target the metrics from a specific Deployment. And those labels exist because during Pod scraping with Prometheus Kubernetes Pod discovery and annotations, Prometheus adds Pod labels to the metric labels. If you want to use custom labelSelector on the query, add the “<strong>metrics.pods.metric.selector</strong>” field to the HPA resource.</p><p>So, we have the custom metrics API, we have configured the Prometheus Adapter to discover and expose some metrics through it, and we have created our first HPA resource. Now it’s time to stress test the deployment and observe the behavior.</p><p>To do so, we’ll introduce you to a tool named <a href="https://github.com/tsenart/vegeta">Vegeta</a> (it’s over 9000!)</p><p>&gt; Vegeta is a versatile HTTP load testing tool built out of a need to drill HTTP services with a constant request rate.</p><p>We’ll use Vegeta to generate load on our application while watching the app’s Pods and the HPA state.</p><pre>kubectl get hpa/&lt;myhpa&gt; -w -n &lt;mynamespace&gt;<br>kubectl get po -l app=&lt;myapp&gt; -w -n &lt;mynamespace&gt;<br>vegeta attack &lt;app_endpoint_http&gt;  <br>(you can also watch events with kubectl get ev -w -n &lt;mynamespace&gt;)</pre><p>Note: you should use 3 separate terminals to achieve that 😛.</p><p>In case your application can handle a huge load and default parameters don’t trigger scaling, you can change some parameters in the Vegeta command. We recommend using workers and rate:</p><ul><li><strong><em>-workers</em></strong>: Initial number of workers (default 10)</li><li><strong><em>-rate</em></strong>: Number of requests per time unit [0 = infinity] (default 50/1s)</li></ul><p>When load increases, your custom metric value will increase too, which should, in turn, trigger a Deployment scale up once the thresholds are met.</p><h3>Using Prometheus Adapter in production</h3><p>As Prometheus Adapter becomes a core component of your architecture, tuning and monitoring it is essential. When HPA is down, there are two potential impacts: you might be using too many resources for the current traffic, or you might not have enough resources to handle the current traffic. In particular, if the Prometheus Adapter Pod fails, your workloads are unaffected immediately; they maintain the last desired replica count calculated by HPA before the adapter went down.</p><p>To prevent this from becoming a single point of failure, make sure to scale Prometheus Adapter’s replicas to a fixed value greater than one, avoiding the default of a single replica. Additionally, you can implement a PodDisruptionBudget to ensure the HPA functionality is not lost during cluster maintenance.</p><p>For effective monitoring, consider using a PodMonitor object if you’re using Prometheus Operator, or leverage Kubernetes service discovery from Prometheus to generate basic metrics on your Prometheus Adapter stack. This allows you to create a Grafana dashboard with critical information. Alternatively, add a probe on the custom metrics API to analyze its output. If you encounter the “Error from server (ServiceUnavailable): the server is currently unable to handle the request” message, it indicates that no Prometheus Adapter Pods are available to serve the API, prompting immediate action.</p><p>By following these steps, you will ensure that your autoscaling infrastructure remains robust and reliable, preventing resource mismanagement and maintaining optimal application performance.</p><h3>Conclusion</h3><p>Horizontal Pod Autoscaling is the standard built-in mechanism in Kubernetes for scaling Deployments, helping your applications handle varying loads efficiently.</p><p>While traditional HPA uses CPU and memory metrics, we’ve shown you how to integrate custom metrics with Prometheus Adapter to enhance this capability, allowing for more precise and relevant scaling decisions.</p><p>Setting up Prometheus and Prometheus Adapter is straightforward and provides significant benefits in terms of flexibility and resource utilization, but configuring it can be a little counterintuitive.</p><p>If HPA with (or without) custom metrics is enough for you, great! For more complex autoscaling scenarios, consider exploring <a href="https://keda.sh/">KEDA (Kubernetes Event-Driven Autoscaling)</a>, another open source project that extends the capabilities of HPA by supporting various event sources and scaling triggers.</p><p>Happy scaling! 😁</p><h3>Additional sources</h3><p>Prometheus Adapter documentation:</p><ul><li><a href="https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/walkthrough.md">General walkthrough</a></li><li><a href="https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/config-walkthrough.md">Configuration</a></li><li><a href="https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/config.md">https://github.com/kubernetes-sigs/prometheus-adapter/blob/master/docs/config.md</a></li></ul><p>Kubernetes HPA documentation:</p><ul><li><a href="https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/horizontal-pod-autoscaler-v2/#HorizontalPodAutoscaler">HPA API</a></li><li><a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior">Configurable scaling behavior</a></li></ul><p>Other:</p><ul><li><a href="https://github.com/tsenart/vegeta">github.com/tsenart/vegeta</a></li><li><a href="https://keda.sh/">KEDA website</a></li><li><a href="https://nodesource.com/blog/event-loop-utilization-nodejs/">Introduction to Event Loop Utilization in Node.js</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a76c1a66ff1c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/optimizing-kubernetes-resources-with-horizontal-pod-autoscaling-via-custom-metrics-and-the-a76c1a66ff1c">Optimizing Kubernetes resources with Horizontal Pod Autoscaling via Custom Metrics and the…</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DILT — S02E07 — Shaker ou la création d’une nouvelle expérience collaborative — Anatomie d’une…]]></title>
            <link>https://medium.com/deezer-engineering/dilt-s02e07-shaker-ou-la-cr%C3%A9ation-dune-nouvelle-exp%C3%A9rience-collaborative-anatomie-d-une-cf844f4ae890?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/cf844f4ae890</guid>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[deez-is-la-tech]]></category>
            <category><![CDATA[product-development]]></category>
            <category><![CDATA[music-recommendations]]></category>
            <category><![CDATA[music-sharing]]></category>
            <dc:creator><![CDATA[Pauline Munier]]></dc:creator>
            <pubDate>Wed, 03 Jul 2024 08:27:25 GMT</pubDate>
            <atom:updated>2024-07-03T08:27:25.415Z</atom:updated>
            <content:encoded><![CDATA[<h3>DILT — S02E07 — <strong>Shaker ou la création d’une nouvelle expérience collaborative</strong> — Anatomie d’une feature</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5wZ2bnFEXv87JgVl49KMTg.png" /></figure><p>Après quelques mois d’absence, notre série “anatomie d’une feature” fait son grand retour à l’occasion d’un épisode sur Shaker, une expérience de partage musical inédite et inclusive dévoilée par Deezer fin 2023.</p><p>Découvrez ainsi les coulisses de la création, du développement et du déploiement de cette nouvelle expérience en compagnie de deux de ses principaux artisans.</p><p>Bonne écoute et/ou bonne lecture !</p><p><em>Note: This post accompanies the release of the seventh (and last) episode of the second season of “Deez is la tech”, a podcast created by Deezer’s Product &amp; Tech teams — in French only for now. You can still find English content on </em><a href="https://deezer.io/"><em>deezer.io</em></a><em> though. Go check it out!</em></p><h3>Résumé de l’épisode</h3><p>En novembre 2023, quelques jours après l’annonce de sa nouvelle identité visuelle, Deezer a dévoilé Shaker, une expérience collaborative musicale inédite qui réunit amis, famille, collègues ou même inconnus autour de leur bande-son idéale et de statistiques musicales, quelle que soit leur plateforme de streaming. Des premières étapes d’idéation à sa sortie officielle, cette nouvelle fonctionnalité a fait l’objet de plusieurs itérations et phases de test avant de prendre sa forme actuelle.</p><p>Comment est né Shaker ? Quelle stratégie a été adoptée pour développer et déployer une telle expérience ? Quels ont été les principaux défis que les équipes produit et techniques de Deezer ont dû adresser pour aboutir à ce résultat ? Quelles sont les pistes d’amélioration future ?</p><p>Dans ce dernier épisode de la saison, Benjamin Chapus (Data Scientist — Team Manager — <a href="https://x.com/xbenji?lang=fr">X</a>) et Pierre Trochu (Head of Product — <a href="https://www.linkedin.com/in/pierre-trochu-2277aa24/">LinkedIn</a>) nous partagent la genèse de Shaker et revisitent les aspirations produit, problématiques techniques et réflexions scientifiques qui ont modelé cette nouvelle expérience.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fembed.acast.com%2F632c77d9a2a9bf001245558b%2F6683fedc04b201dd34f3152e&amp;display_name=Acast&amp;url=https%3A%2F%2Fembed.acast.com%2F632c77d9a2a9bf001245558b%2F6683fedc04b201dd34f3152e&amp;image=https%3A%2F%2Fthumborcdn.acast.com%2FOMmhur2DGRIb32EV4Mj2z_n1JFA%3D%2F350x350%2Fhttps%253A%252F%252Fassets.pippa.io%252Fshows%252F632c77d9a2a9bf001245558b%252F1705427540204-371d6018382bd843f94dbfbb28c9290e.jpeg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=acast" width="600" height="180" frameborder="0" scrolling="no"><a href="https://medium.com/media/2fc7a310c3215edcd99ef7a82cfb2e8e/href">https://medium.com/media/2fc7a310c3215edcd99ef7a82cfb2e8e/href</a></iframe><p><em>Épisode disponible également sur </em><a href="https://deezer.page.link/kbJx3HSwRbHjn2m87"><em>Deezer</em></a><em> | </em><a href="https://podcasts.apple.com/fr/podcast/deez-is-la-tech/id1648116961?i=1000660963517"><em>Apple Podcasts</em></a><em> | </em><a href="https://open.spotify.com/episode/6mwHqDJGvzzHKzjg00xqdo?si=atO9i9HKQOCqtdIOWn33kQ"><em>Spotify</em></a><em> | </em><a href="https://music.amazon.com/podcasts/c0253c4d-c100-46d9-a660-d1b5f93cc69f/episodes/2f32bb28-6649-4acb-8d0e-1dc0502e6914/deez-is-la-tech-s02e07---shaker-ou-la-cr%C3%A9ation-d&#39;une-nouvelle-exp%C3%A9rience-collaborative-anatomie-d&#39;une-feature"><em>Amazon Music</em></a><em>.</em></p><h3>Transcription</h3><p><strong>[00:00:02.050] — Vincent : </strong>Bonjour et bienvenue dans Deez is la tech, le podcast qui ne pète ni les plombs, ni les crons ! Créé et animé par les équipes Product &amp; Tech de Deezer, ce programme aborde des sujets relatifs aux mondes de la tech et du streaming musical, et vous fait occasionnellement découvrir les coulisses de certaines des fonctionnalités phares de Deezer. Rejoignez-nous chaque mois pour une nouvelle discussion entre collègues et pairs, en toute décontraction, mêlant partages d’expériences, bonnes pratiques et réflexions sur les tendances futures. Prêts pour un nouvel épisode ? Chaussez vos écouteurs, ça commence maintenant !</p><p><strong>[00:01:25.000] — Vincent : </strong>N’avons-nous pas tous souhaité un jour pouvoir créer, en un claquement de doigts, la playlist idéale pour une soirée d’anniversaire ou un voyage entre amis ? C’est, entre autres, pour répondre à ce besoin que l’idée d’une nouvelle fonctionnalité collaborative a commencé à émerger au sein des équipes Product &amp; Tech de Deezer. Fin 2023, après plusieurs mois de travail, Deezer dévoilait ainsi Shaker, une expérience musicale conviviale inédite réunissant amis, famille, collègues ou même inconnus autour de leur bande-son idéale et de statistiques musicales, quelle que soit leur plateforme de streaming. Mais comment est né le concept de Shaker ? Quelle a été la stratégie de développement et de déploiement de cette nouvelle expérience ? Quels ont été les principaux défis et problématiques à adresser pour aboutir à ce résultat ? Et enfin, maintenant que l’expérience est disponible pour tous, quelles sont les évolutions prévues pour l’enrichir davantage ? Dans le cadre de notre série “anatomie d’une feature”, Pauline et moi accueillons aujourd’hui Benjamin Chapus et Pierre Trochu afin d’explorer la genèse et les dessous de Shaker, entre aspirations produit, contraintes techniques et réflexions scientifiques. Bonjour à tous les deux !</p><p><strong>[00:01:52.310] — Pierre : </strong>Bonjour à tous, je suis Pierre Trochu, Head of Product pour les expériences du produit Deezer. Mon rôle consiste, avec toute une équipe, à enrichir la manière dont on interagit avec la musique dans le produit Deezer. Parce qu’aujourd’hui, une application de musique, ce n’est pas que du streaming. Vous avez aussi des podcasts, des places de concert à acheter ou à gagner, des statistiques sur votre usage, des jeux et surtout, différentes façons de se partager la musique et de se décrire musicalement. Et c’est de ça dont on va parler aujourd’hui avec Shaker.</p><p><strong>[00:02:18.600] — Benjamin : </strong>Bonjour à tous, je m’appelle Benjamin. Je dirige l’équipe Recommandation chez Deezer, l’équipe en charge de tous les algorithmes de machine learning sur les principales fonctionnalités de recommandation comme le Flow, les playlists automatiques, les mix artistes, les artistes similaires et aussi, plus récemment, sur Shaker, pour créer des mix de groupes. On va voir comment ça marche.</p><p><strong>[00:02:37.440] — Vincent : </strong>On commence par une première question assez simple : c’est quoi Shaker ?</p><p><strong>[00:02:41.800] — Pierre : </strong>Si je devais pitcher Shaker quelque part en soirée, je dirais : partage tes sons avec tes amis, quelle que soit votre app de musique. Shaker, c’est plutôt quelque chose que l’on a pensé d’abord pour les amis, pour les groupes d’amis qui existent déjà. Il y a l’idée de se partager des chansons facilement, de créer des playlists ensemble, mais aussi de partager qui l’on est musicalement. Pour ça, on propose, dans l’expérience Shaker, trois grosses fonctionnalités. Il y a d’abord un mix. C’est une recommandation de 70 chansons que l’on peut ajuster assez facilement grâce au travail de la Reco de Deezer. Un mix, c’est une liste de chansons qui représente les goûts de chacun des membres du groupe d’amis que vous avez rejoint dans Shaker. La deuxième fonctionnalité de l’expérience est une playlist collaborative. Ça permet aux gens qui savent déjà ce qu’ils veulent de construire facilement une playlist de chansons, quelle que soit l’app de musique. Donc c’est une playlist que l’on peut consommer que l’on soit chez Spotify ou chez Deezer. Et la troisième fonctionnalité, c’est plus pour décrire musicalement qui est le groupe et qui sont les personnes qui composent le groupe. Ce sont des statistiques musicales un peu amusantes sur l’identité musicale du groupe, mais aussi des statistiques sur chaque membre du groupe d’amis.</p><p><strong>[00:03:46.820] — Pauline : </strong>Comme c’est assez nouveau comme expérience — en tout cas, de cette ampleur-là sur Deezer — comment l’idée est venue ? Quelle est la genèse du projet ? Pourquoi vous vous êtes dit à un moment donné : “on va faire ça” ?</p><p><strong>[00:04:00.520] — Pierre : </strong>Il y a vraiment plein de sources de data qui sont venues informer la réflexion sur Shaker. Déjà, le marché du streaming est assez fragmenté, donc tout le monde n’est pas sur Deezer — malheureusement — et tout le monde n’est pas sur Spotify — heureusement. Vous avez, sans doute, dans votre entourage, des amis qui ne sont pas sur la même app de musique que vous. Ça, c’est la donnée de départ. Par ailleurs, les Product Managers, les Product Designers et les User Researchers qui ont travaillé sur Shaker ont plusieurs façons de réfléchir. Ils regardent la data. Donc on a regardé un peu comment les gens faisaient pour se partager des liens de musique entre applications de streaming. Ce que l’on voit, c’est que c’est assez ouvert. Si vous partagez un lien Deezer à des amis, ce ne sera pas que des gens sur Deezer qui vont ouvrir ce lien. Il y a vraiment une envie de consommer la musique, même si elle est sur Deezer et pas sur Spotify. On a aussi fait pas mal d’entretiens utilisateurs avec des utilisateurs plutôt autour de la tranche d’âge 18–24 ans pour comprendre un peu quels étaient leurs “pain points”, leurs problèmes quand il s’agit de partager de la musique. Ils nous ont fait remonter trois types de problèmes. Le premier problème, c’est que, comme je viens de le mentionner, tous leurs amis ne sont pas nécessairement sur la même app de streaming. Ça rend le processus de partage un peu plus pénible. Le deuxième problème, c’est que les gens pensent qu’ils n’ont pas les mêmes goûts. Ils ont besoin d’un référent commun pour partager un peu plus que les mêmes choses, donc ils ont besoin qu’une app de streaming automatise un peu ce partage de musique pour trouver des points communs. Et le troisième problème que l’on a identifié et qui est plus spécifique à cette tranche d’âge, c’est qu’aujourd’hui, certains ont remonté qu’ils ont plutôt la flemme de faire des playlists. Ils aimeraient bien avoir quelque chose d’un peu plus automatique pour pouvoir trouver facilement quoi écouter quand vous êtes ensemble dans la voiture ou en soirée. Ce sont les trois problèmes que l’on a identifiés pour les utilisateurs quand on les a interviewés. Donc, il y a la data, il y a ces problèmes-là, et puis les Product Managers ont aussi un peu regardé ce qui se faisait sur le marché autour de cette façon de se partager la musique. Et ce qu’on observait depuis deux-trois ans, c’est qu’il y a souvent des petites apps qui se lancent pour adresser ce problème précis, qui est : “c’est un peu pénible de se partager de la musique ensemble”. On a vu plusieurs applications sur l’App Store qui se lançaient avec des concepts un peu similaires où l’on peut rejoindre un groupe pour se partager de la musique, quelle que soit son app de streaming. On observait que ces petites applications arrivaient à quelque chose mais qu’à chaque fois, l’usage redescendait, peut-être parce qu’il n’y avait pas assez de masse critique en termes de volume pour vraiment décoller. Du coup, on s’est dit : “Ça, c’est un signal encourageant pour aller plus loin, justement, sur le partage universel de la musique”.</p><p><strong>[00:06:36.670] — Vincent : </strong>OK. Du coup, j’imagine que ça n’a pas été facile d’intégrer ça dans une application de streaming musical comme Deezer. Quelle était la stratégie au niveau du développement de la feature ? Comment l’avez-vous amenée petit à petit ou est-ce que vous l’avez fait apparaître d’un seul coup ? Comment avez-vous géré ça ?</p><p><strong>[00:06:54.040] — Pierre : </strong>C’est une question très intéressante. On a eu une stratégie de “MVP” avec Shaker. Un “MVP” chez Deezer, c’est un concept que l’on cherche à valider parce qu’il y a une hypothèse risquée derrière. La première hypothèse risquée derrière Shaker, c’est : est-ce que les utilisateurs vont avoir envie de partager la musique avec certains de leurs amis qui ne sont pas chez Deezer ? Parce qu’en fait, tous nos utilisateurs ont un référentiel commun. Ils savent qu’ils utilisent une app de streaming, que c’est plutôt des environnements fermés — à dessein, puisque c’est comme ça que le business marche. Pour ça, c’était vraiment super intéressant, ce MVP. On s’est vraiment concentrés sur le fait de confirmer cette hypothèse. Oui, les utilisateurs sont prêts à partager de la musique ou à créer un espace commun de partage de musique avec des utilisateurs qui ne sont pas chez Deezer. On a eu une stratégie de “soft launch”, donc on a choisi trois pays sur lesquels on pouvait lancer sans trop de volume et sans trop de communication et on a sorti MVP pendant l’été 2023. Ce que l’on a vu, c’est qu’il fallait aller encore plus loin sur cette hypothèse parce que beaucoup d’utilisateurs, lorsqu’ils ont découvert la fonctionnalité dans ces pays du soft launch, sont tellement loin en termes de référentiel par rapport à la proposition de valeur de Shaker — qui est “partage tes sons, quelle que soit ton app de musique” — et n’ont pas compris qu’ils pouvaient partager de la musique avec leurs amis chez Spotify, chez YouTube Music, chez Apple Music, etc. C’est amusant, il y a même des utilisateurs qui, alors que la fonctionnalité est construite pour ça, ont partagé des liens d’invitation à Shaker dans leur groupe WhatsApp avec leurs amis en disant : “Je vous partage ça mais c’est que pour les gens qui sont chez Deezer”. Or, il était marqué explicitement que la fonctionnalité est construite pour adresser ce problème. C’était vraiment super intéressant de faire cette phase de soft launch puisque l’on a vu qu’il fallait aller plus loin sur le MVP avant d’internationaliser la fonctionnalité. On n’était pas encore assez clair sur ce côté universel, tellement ça repousse une compréhension que l’on a tous du fonctionnement d’une app de streaming, qui est : c’est un périmètre, un écosystème fermé qui ne permet pas de partager. La stratégie a vraiment été de confirmer cette hypothèse risquée, et pour le MVP, on s’est plutôt concentré sur l’entrée de l’expérience. On n’a pas construit tout ce que je viens de décrire en début d’émission, à savoir les stats dans les détails, etc., on s’est plutôt concentré sur cette idée : oui, confirmons que des utilisateurs sont prêts à se rejoindre ensemble dans un espace commun, quelle que soit leur app de musique. Ça veut dire que l’on a fait un MVP qui a pris un peu plus de temps que prévu. Par exemple, on a choisi de toujours garder dans l’équation l’idée qu’il faut qu’il y ait des utilisateurs de Spotify, de YouTube Music, d’Apple Music qui puissent rejoindre cette fonctionnalité, alors qu’on aurait très bien pu faire une fonctionnalité où l’on permet juste à des Deezer users de se partager des statistiques musicales, etc. On s’est vraiment concentrés sur cette hypothèse risquée-là, et je pense que c’était une bonne idée parce que l’on a justement vu avec la première version — donc le MVP que l’on a sorti dans dans ces trois pays en soft launch — que ce n’était pas si évident pour les utilisateurs de comprendre le cœur de cette proposition de valeur. Entre le MVP et la sortie officielle en grande pompe en novembre, il s’est découlé quatre mois. On a mis vraiment le paquet sur cet axe-là en s’éloignant un peu d’un langage textuel et en passant beaucoup plus par un langage visuel pour mieux exprimer cette idée d’inclusivité et d’universalité, qui est le point différenciant de cette fonctionnalité. C’était satisfaisant parce que l’on a vu une vraie différence en termes de pourcentage d’utilisateurs qui ne sont pas chez Deezer entre le MVP, où ce pourcentage était proche de zéro, et le lancement de la fonctionnalité, où il y avait un pourcentage autour de 30% des utilisateurs qui n’étaient pas chez Deezer — donc chez Spotify, chez YouTube Music, chez Apple Music, etc.</p><p><strong>[00:10:49.880] — Pauline</strong></p><p>Tu dis que c’est passé par une manière visuelle d’expliquer, de faire mieux comprendre toute la fonctionnalité, mais concrètement, vous avez fait quoi ? Vous avez changé les designs ? Vous avez mis des smileys qui sourient et qui disent “viens chez nous, on est ouvert” ou …?</p><p><strong>[00:11:10.530] — Pierre : </strong>Avant, on communiquait uniquement dans le texte. Et là, on a revu le parcours d’onboarding pour élaguer un peu la charge cognitive qui peut reposer sur l’utilisateur pour comprendre ce que c’est, en découpant un peu plus les étapes de façon à ce que les users entrent dans un tunnel qui leur donne envie, au moins, de finir tout le process, et en utilisant des couleurs, des formes qui évoquent beaucoup plus l’idée d’universalité. On a pu utiliser le vert et le rouge, qui correspondent aux couleurs de deux de nos concurrents, — les deux concurrents les plus féroces, YouTube et Spotify — pour mieux évoquer cette idée d’universalité et de partage, quelle que soit l’app de musique. Donc passer par un langage beaucoup plus visuel et beaucoup moins textuel. Des fois, les choses simples sont les meilleures et ça a suffit pour faire exploser le pourcentage de non-Deezer users dans la fonctionnalité.</p><p><strong>[00:12:00.420] — Pauline : </strong>Et justement, c’est un peu étonnant d’ouvrir une partie de son produit, de ses fonctionnalités à des gens qui ne sont pas abonnés, qui n’utilisent pas notre service. Ce n’est pas quelque chose qui vous a inquiété en se disant que les gens qui sont abonnés vont peut-être, du coup, partir ailleurs et se dire : “je n’ai pas de raison de rester si je peux faire des choses, même en n’étant pas abonné…” ?</p><p><strong>[00:12:23.300] — Pierre : </strong>Non, il y a vraiment plein de côtés positifs à l’idée derrière Shaker. Déjà, pour nos utilisateurs, ça améliore leur expérience dans le produit. Pour eux, c’est plus facile de partager avec leurs amis, quelle que soit l’app de musique qui est utilisée par leurs amis. Ça, c’était vraiment un “no-brainer” pour nous, ça fait sens de faire ça. Ensuite, on voit petit à petit que cette barrière entre apps de streaming fait de moins en moins sens. Il est probable, déjà, que cette barrière explose complètement avec l’émergence des capacités de l’IA dans les téléphones. Par exemple, le nouveau Siri semble être beaucoup plus à même de faire des actions pour l’utilisateur, quel que soit le contexte. Donc il est probable que ce nouveau Siri, qui sort en Q4 2024, permette à un utilisateur d’iPhone de regarder sa conversation WhatsApp, de lui dire “ouvre-moi ce lien Spotify dans l’app Deezer” et que la chanson soit ouverte dans Deezer. Il est très probable que cette barrière ne fasse plus sens et que ça devienne un standard du marché. Et puis surtout, ce n’est pas ça qui fait la valeur d’une application de streaming, le fait de pas pouvoir partager ses chansons. Il y a d’autres critères qui créent la rétention. Et par rapport au non-Deezer users, pour nous, c’est une bonne façon aussi d’exposer Deezer, de faire re-goûter le produit. Et justement, cette fonctionnalité a été lancée à un moment où l’on a “rebrandé” la marque Deezer. On a repositionné le produit Deezer autour de deux valeurs émotionnelles assez claires : la connexion et l’expression. Et cette fonctionnalité était parfaite pour ce nouveau positionnement de marque puisqu’elle permet de se connecter avec d’autres utilisateurs et de s’exprimer, en fait. Avec tout le travail, justement, que font Benjamin et son équipe, on arrive à décrire de façon humaine des statistiques mathématiques sur les utilisateurs. On leur permet de s’exprimer, d’être plus à l’aise avec leurs goûts. Donc ça faisait vraiment sens pour Deezer, à ce moment-là, et par rapport aux évolutions du marché, d’être le premier à aller plus loin sur l’aspect partage universel. Surtout que, comme je vous le disais, il y a déjà des petites applications qui se montent autour de ce “pain point” ou problème utilisateur. Donc autant que l’un des gros acteurs du marché prenne le problème à bras le corps et aille plus loin sur cet aspect.</p><p><strong>[00:14:50.080] — Vincent : </strong>Je propose maintenant d’ouvrir un petit peu le capot et de regarder un peu comment ça a été fait et comment on est arrivé à cette solution, puisque j’imagine que le fait d’essayer de faire matcher des gens qui viennent d’applications différentes n’a pas dû être simple. Comment a-t-on fait, par exemple, ne serait-ce que pour faire matcher les catalogues qui viennent de différents endroits ? Comment peut-on savoir qu’un utilisateur qui est chez Spotify écoute de la musique qui correspond à telle ou telle track dans le catalogue de Deezer ? Comment a-t-on réussi à faire ça ?</p><p><strong>[00:15:23.320] — Benjamin : </strong>Il me semble que pour le matching du catalogue, on passe par un partenaire, TuneMyMusic, qui va faire la jonction entre les deux plateformes. Ils ont tout un tas d’API qui sont déjà existantes, qui permettent de connecter aux comptes Spotify, Apple Music ou Deezer. Il me semble qu’ils maintiennent eux-mêmes une correspondance dans le catalogue entre les ID Spotify et les ID Deezer, ce qui évite de devoir le faire nous-mêmes. C’est une tâche assez compliquée de matcher, juste avec un titre d’artiste, de chanson, de trouver dans Deezer quelle est la vraie chanson que l’on veut écouter, tout simplement parce qu’il peut y avoir des doublons, il peut y avoir des live… Il ne suffit pas de faire juste une recherche, il faut s’assurer qu’avec cette chanson-là, on a le bon artiste, le bon album et tout ça. C’est assez pénible à faire et en passant par un partenaire qui s’occupe de faire ça, c’est beaucoup plus simple. Et ça permet aussi d’accélérer cet onboarding : quand une personne va connecter son compte Spotify, on va directement avoir la relation avec les chansons chez Deezer, dans le catalogue, et ne pas avoir à passer sur chaque chanson pour trouver l’équivalent. Il me semble que ça marche comme ça, en tout cas. Pierre peut confirmer peut-être ?</p><p><strong>[00:16:34.100] — Pierre : </strong>Tout à fait, oui. On passe par un partenaire qui s’appelle TuneMyMusic, avec qui l’on travaille depuis des années, qui permet à n’importe qui d’importer, lorsqu’il rejoint Deezer, sa librairie existante chez Spotify, chez YouTube, chez Apple Music. Donc si vous êtes chez Spotify depuis 10 ans, vous pouvez quand même très facilement, dès le premier jour chez Deezer, importer l’intégralité de votre catalogue musical construit chez Spotify.</p><p><strong>[00:16:57.640] — Benjamin : </strong>Je trouve ça assez malin parce qu’il y a un objectif un peu caché, qui est de montrer : “regardez, ce n’est pas si compliqué, finalement, de passer d’une plateforme à l’autre”. Alors, c’est à double tranchant : on peut le faire dans un sens, on peut le faire dans l’autre. Mais je pense que beaucoup de personnes se sentent attachées à leur distributeur de musique actuel parce qu’elles y ont toutes leurs playlists. Elles peuvent aussi être attachées à l’algo, à certaines fonctionnalités, mais globalement, il y a surtout ce travail de collection qui est vraiment très important pour les utilisateurs et utilisatrices.</p><p><strong>[00:17:24.480] — Vincent : </strong>C’est un peu la notion que l’on essaie d’avoir en ce qui concerne la portabilité des numéros de téléphone chez les opérateurs mobiles, finalement.</p><p><strong>[00:17:31.200] — Benjamin : </strong>Voilà, exactement. Si, quand on changeait d’opérateur, il fallait re-rentrer tout son répertoire, les gens bougeraient beaucoup moins, je pense. Heureusement, les téléphones enregistrent tout ça. Donc oui, ça permet de montrer que l’on peut passer de l’un à l’autre, qu’elles sont assez perméables, ces frontières entre les plateformes. Je trouve ça assez malin d’avoir permis à des personnes de Spotify de rentrer dans le monde Deezer et de leur montrer : “regardez, vous allez retrouver toutes vos top chansons, vos playlists, vos écoutes du moment”. Et c’est ça qui va servir à calculer le profil de ce nouvel utilisateur lorsqu’on va vouloir créer ce mix de groupe pour Shaker.</p><p><strong>[00:18:07.630] — Pauline : </strong>Justement, comment faites-vous pour créer ce mix avec toutes les données de tous les utilisateurs d’un groupe Shaker ?</p><p><strong>[00:18:15.780] — Benjamin : </strong>C’est assez complexe. Finalement, pour Shaker, il y a différentes fonctionnalités que l’on a essayé d’implémenter dans mon équipe, à la Recommandation. Le premier truc, c’était d’abord de calculer un score d’affinité pour dire à quel point deux personnes sont compatibles, musicalement parlant, ou à quel point elles se correspondent dans leurs écoutes. Et en fait, c’est beaucoup plus compliqué que ce qu’on imagine. Déjà, l’affinité et la compatibilité, ce n’est pas tout à fait pareil. On peut dire : “OK, on écoute exactement les mêmes choses, Pierre et moi, mais je peux peut-être trouver une troisième personne avec qui l’on écoute des choses différentes, mais avec qui l’on se retrouve, quand même, sur quelque chose de similaire”. C’est-à-dire que peut-être qu’il y a des genres que j’écoute, moi, qu’une autre personne n’écoute pas, mais on a un genre en commun. Est-ce que ça veut dire que l’on a des bonnes affinités ? Est-ce que ça veut dire que l’on est compatibles ? Ça, c’était déjà un problème sur lequel on a pas mal planché. Quand on veut calculer un score comme ça, la première chose qui vient à l’esprit, c’est de regarder les artistes ou les chansons, les albums que l’on a en commun. C’est l’approche la plus directe et la plus simple. Ça peut marcher dans pas mal de cas, mais pas dans tous. On a très rapidement vu les limites. Il y a des artistes qui sont très répandus, très écoutés — tout le monde écoute ou a au moins écouté un petit peu ou en partie, je ne sais pas, Beyoncé, une chanson de Jul ou Coldplay — et si l’on vous dit “vous avez ces quatre artistes en commun”, vous n’allez quand même pas avoir l’impression que vous êtes compatibles ou avoir une grosse affinité musicale particulière. Donc finalement, on s’est dit : “tiens, peut-être qu’il faudrait que l’on mette un peu plus l’accent sur les groupes qui sont moins connus, qui sont un peu plus dans la fin de la traîne de ce que l’on écoute”. Si l’on se retrouve avec un artiste vraiment pas connu, on se dit : “ah oui, effectivement, on a les mêmes goûts”. Ou alors : “tu connais ça, toi aussi ? C’est super”. Et là, on a l’impression d’être un peu plus proches. Donc on a essayé de pondérer un petit peu plus les artistes moins connus, les albums un peu moins populaires. La deuxième chose, c’est que peut-être que deux personnes écoutent du métal, par exemple, mais n’écoutent pas les mêmes artistes. Je n’ai pas d’exemple en tête mais on peut avoir un set d’artistes complètement disjoints et pourtant écouter le même genre musical. À ce moment-là, on n’a pas envie de dire que la compatibilité musicale est nulle, vu que l’on écoute quand même le même genre d’artistes. Donc, il a fallu que l’on utilise des données un peu plus précises comme le genre musical, par exemple. En fait, on va utiliser des data… On parle d’”embeddings” à la Recommandation. C’est une représentation mathématique un peu plus dense qui encode dans un grand vecteur les affinités musicales, l’identité, le “fingerprint” musical de la personne. Et l’avantage de cette représentation, c’est que l’on peut comparer ces deux empreintes et calculer une distance entre ces deux utilisateurs, entre ces deux profils d’écoute. Ça va nous donner un score que l’on peut normaliser entre 0 et 1, par exemple. On va pouvoir dire : “on est compatible à 25%, même si l’on n’écoute pas du tout les mêmes choses, mais parce que l’on est globalement dans la même région du catalogue qui est écoutée”. Donc le score est calculé comme ça. Des fois, on a une belle théorie mathématique derrière, on est tout content et on va voir Pierre, et il nous dit : “non mais regardez, ces deux profils, pourtant, c’est moi et c’est moi aussi, le deuxième profil chez Spotify, et je n’ai pas du tout le bon score. Qu’est-ce qui se passe ?” Évidemment, on retravaille un petit peu ces scores-là, on étalonne afin que ça ressemble le plus possible à ce que l’on s’est imaginé. Mais il y a toujours une grosse distorsion entre ce que l’on pense écouter, ce que l’on écoute vraiment et la proximité musicale que l’on a avec quelqu’un. Le sujet n’est pas simple mais on a, je pense, à peu près trouvé quelque chose qui avait du sens pour les utilisateurs et pour nous.</p><p><strong>[00:21:47.590] — Pierre : </strong>Pour rebondir sur ce que dit Benjamin, c’est ça que j’ai trouvé très amusant sur cette expérience, c’est cette idée qu’ont les utilisateurs d’eux-mêmes. C’est intéressant de la représenter mathématiquement et de leur permettre d’aller un peu au-delà. Je vous parlais en début d’émission de ce présupposé des utilisateurs qui pensent que leurs amis n’écoutent pas la même chose qu’eux ou qu’ils ne sont pas compatibles. On a pu observer que les utilisateurs sont surpris par l’expérience, des fois, et sont contents de se découvrir des points communs qu’ils n’imaginaient pas dans leurs catalogues musicaux. C’est vraiment super intéressant et c’est là où je trouve que le produit crée une vraie valeur. Ça permet d’aller un peu au-delà de ses présupposés. Dans une app de musique, si l’on ne fait que du streaming, on a tendance, parfois, à s’enfermer dans une boucle où la sérendipité, le côté découverte, n’est pas suffisant. Or, nous, en tant qu’app de musique, de produit musical, là où l’on crée le plus de valeur, c’est lorsque l’on permet à des utilisateurs de découvrir autre chose que leur univers quotidien. Donc c’est vraiment génial de le faire avec cet angle un peu plus émotionnel, des représentations de leurs amis, etc., de les aider à sortir un peu de leur zone de confort musicale avec, en plus, des statistiques qui sont amusantes à consommer la première fois — c’est toujours amusant de voir à quel point vous êtes compatibles avec votre compagne, vos parents, votre fils, votre fille, etc.</p><p><strong>[00:23:10.870] — Vincent : </strong>Du coup, vous avez, j’imagine, dû itérer pas mal sur l’algorithmique. Il vous a fallu, en gros, à peu près combien d’itérations ? Est-ce que vous êtes encore en train de travailler sur ces scores de compatibilité et sur ces mix de groupes ?</p><p><strong>[00:23:26.210] — Benjamin : </strong>Oui, c’est quelque chose que l’on continue de faire évoluer puisque récemment, il y a eu l’introduction de stories dans un mix Shaker, dans un groupe Shaker. On a différentes slides qui apparaissent et qui vont mettre en avant certaines caractéristiques du Shaker, comme les artistes que l’on a en commun ou les chansons que l’on a en commun. Et le fait d’avoir exposé ça vient un petit peu challenger le score que l’on a calculé puisque parfois, on se dit “on a 70% de compatibilité et on n’a que deux artistes en commun”, et on se dit : “ah, je suis un peu déçu, je pensais que l’on aurait beaucoup plus de terrain commun entre deux personnes”. C’est un peu les exemples que je donnais tout à l’heure. Ça va au-delà du simple overlap de librairie, il s’agit aussi de comprendre comment marchent les goûts. Donc on essaie de faire correspondre les data que l’on expose, qui n’étaient pas forcément exposées au début, avec ce score-là pour qu’il colle au mieux avec ce que l’on s’imagine par rapport à sa compatibilité. Et là, on n’a parlé que du score, alors que je dirais que l’ADN de Shaker, c’est de créer un mix, une playlist. Là, le challenge est encore plus important, je dirais. En fait, on a construit ça sur les bases que l’on avait déjà développées pour le Family mix. On avait — et on a toujours — une offre Famille chez Deezer et on a la possibilité d’avoir une playlist qui s’adapte automatiquement à tous les comptes du compte Famille. C’était exactement le même problème de départ. On a une, deux, trois, quatre, cinq, six personnes avec leurs habitudes musicales. Comment on crée le mix parfait, celui que l’on pourrait écouter si on était tous dans une voiture ou dans la même soirée et qui conviendrait au plus de monde possible ? C’est un problème d’un point de vue technique et algorithmique. C’est un problème d’optimisation multitenant. Il faut essayer de trouver ce qui va augmenter la satisfaction de tout le monde, avec le risque de ne pas tomber dans un ventre mou qui plaît un petit peu à tout le monde. Il faut arriver à doser entre “on met des choses qui sont assez propres à un profil, mais qui ne sont quand même pas trop éloignées, qui ne vont braquer le reste” plutôt que de chercher vraiment quelque chose qui va être au milieu et qui convient moyennement à tout le monde. Pour faire ça et pour expliquer simplement, on peut imaginer les goûts des utilisateurs comme une carte en deux dimensions avec des zones activées. “Moi, j’écoute ça, j’écoute ça”… Après, on peut essayer de superposer ces N cartes les unes au-dessus des autres et voir les endroits où l’on a des superpositions, tout simplement, ou voir si l’on a des points qui sont pas trop éloignés des endroits activés par les membres du groupe. Ce qui est pas mal quand on a fait ça, la bonne surprise, c’est que parfois, on peut avoir une personne qui écoute, disons, du rap et trois autres personnes qui n’en écoutent jamais, et on va quand même arriver à recommander du rap. Ce sera peut-être du rap qui va être perçu comme plus accessible pour les personnes qui n’en écoutent pas, et ça permet d’isoler, des fois, des morceaux, des groupes de musique, qui sont une bonne introduction dans ce genre musical, si on ne l’écoute pas. Je prenais l’exemple du métal tout à l’heure : peut-être que l’on va recommander des chansons de Metallica, qui est un groupe de métal assez mainstream, là où la personne qui a amené le métal écoutera des groupes beaucoup plus confidentiels. Mais ça permet quand même d’avoir quelque chose d’un peu plus populaire et écoutable par tout le monde et ça permet de faire découvrir son profil et son type de musique aux autres personnes. Ça, ça ne marchait pas trop mal dans le mix Famille, donc c’est quelque chose que l’on a généralisé ensuite pour Shaker. La grosse différence entre ces deux algos, c’est que pour le mix Famille, c’est quelque chose que l’on calculait de manière offline, que l’on actualisait chaque semaine ou chaque jour, alors que pour Shaker, il fallait que ce soit instantané et que ça réponde en quelques secondes. Et ça, c’était un gros challenge à relever, un gros défi technique.</p><p><strong>[00:27:09.480] — Vincent : </strong>Du coup, ce genre de mix n’est pas juste une liste de tracks qui ont été écoutées un peu par tout le monde, mais ça peut finalement amener à des découvertes, même pour tous les membres du groupe.</p><p><strong>[00:27:22.800] — Benjamin : </strong>Dans la première version, le but était d’aller uniquement piocher dans des choses qui sont connues et écoutées par chaque membre du groupe. Donc on n’introduit pas de nouvelles chansons extérieures inconnues de toutes les personnes. C’est plutôt aller sélectionner un sous-ensemble de chansons chez chaque personne pour créer une playlist cohérente. Moi, je m’en suis servi récemment. J’étais parti avec des amis en week-end, on avait loué une baraque. On a chacun nos écoutes un peu propres et on ne savait plus quoi écouter à un moment. On avait fait un Shaker à un moment, qu’on a un petit peu réactivé, et on était agréablement surpris par le fait que chaque chanson nous plaisait. Chacun pouvait parler de la chanson, savait d’où elle venait, et on essayait même de deviner : “Ça, c’est toi ? Non, ça, c’est moi ?” D’où ça venait ? C’est assez ludique, assez rigolo. Rapidement, on s’est rendu compte que peut-être que l’on a rapidement fait le tour de la librairie de chacun des utilisateurs et que l’on a envie de se dire : “ce Shaker, j’ai envie qu’il vive, qu’il s’auto-alimente et que l’on vienne me rajouter, périodiquement, des nouvelles chansons qui auront cette même contrainte, qui doivent plaire à tout le monde ou au plus grand monde, en tout cas”.</p><p><strong>[00:28:41.510] — Pauline : </strong>On a un peu parlé des challenges et des difficultés côté Reco. Est-ce qu’il y a eu aussi, côté Produit, des défis particuliers ? Peut-être, justement, que l’on a une app — je pense à ça mais ça peut être autre chose — qui est déjà assez complète, assez grande, comment on intègre Shaker dans une app qui a déjà cinq onglets, qui a déjà une myriade de fonctionnalités ? Est-ce que ça a été un sujet pour vous ? Est-ce qu’il y a eu d’autres choses sur lesquelles vous vous êtes posés des questions, des choses que vous avez abandonnées parce que vous n’arriviez pas à les faire fitter quelque part ? Ou alors, vous avez eu des retours, ça n’a pas marché ?</p><p><strong>[00:29:23.650] — Pierre : </strong>Moi, ce que je retiens vraiment, c’est ce dont j’ai un peu parlé tout à l’heure, c’est le challenge autour de l’onboarding, donc la façon dont on peut embarquer des utilisateurs qui ne sont pas chez Deezer. Déjà, il y a un challenge de base, c’est que l’on a constaté quand on faisait des tests produit avec les prototypes que ça prend du temps d’importer sa librairie de musique. Il y a une loi d’UX qui dit que, généralement, pour une interaction avec une interface, le temps de réponse doit être autour de 0,4 seconde. Sans ça, l’utilisateur s’impatiente. Or là, imaginons que vous vous connectez avec votre compte Spotify. Si vous avez dix ans d’historique dans Spotify, ça peut mettre deux minutes avant de réussir à atteindre le “Aha moment”, à savoir la valeur de Shaker. Il a fallu travailler un peu dessus, retravailler l’onboarding. J’avais parlé un peu de façon visuelle pour bien expliquer, mais aussi pour donner l’énergie à l’utilisateur d’attendre, d’être patient par rapport à ce qu’on lui a promis, à savoir sa compatibilité musicale avec ses amis. Il n’a pas envie d’attendre trop longtemps pour la connaître. Et pour écouter le mix, il n’a pas non plus envie d’attendre trop longtemps. Ça, c’est vraiment un challenge qui m’a marqué. À la fois, il faut vraiment simplifier la proposition de valeur pour qu’elle soit claire et facile à comprendre, et d’un point de vue purement IT, il faut que ce soit assez rapide parce qu’aujourd’hui, nos utilisateurs sont habitués à des interfaces très rapides et ils ne prendront pas le temps d’attendre. C’était assez intéressant de détailler tout ce qui se passe dans l’entonnoir lors de cet onboarding et de travailler petit à petit sur chacun des compromis que l’on avait faits en V0 pour permettre aux utilisateurs d’aller le plus vite possible. Typiquement, au début, ce n’était pas possible de rejoindre un Shaker en utilisant le single sign-on, donc la possibilité de se connecter juste avec son Gmail ou avec son Facebook. C’est l’une des premières fonctionnalités que l’on a ajoutées — parce qu’avant, il fallait demander aux utilisateurs de mettre leur email, etc. Voilà, c’est vraiment le challenge que j’ai identifié autour des non-Deezer users, donc travailler sur toute cette partie onboarding pour leur permettre de facilement débloquer et consommer la valeur de Shaker. Ensuite, effectivement, comment est-ce que l’on intègre Shaker dans une app de musique ? C’est un travail au long cours, on n’a pas fini, mais il faut proposer Shaker au bon moment, dans les bons contextes pour nos utilisateurs. Ce n’est pas quelque chose que vous avez envie de faire tout le temps, toujours, avec tous vos amis. Il y a un travail à faire en collaboration avec les Product Marketing Managers pour proposer l’expérience au bon moment. Typiquement, récemment, on s’est dit : “il y a les longs week-ends qui arrivent en France”. Du coup, on a fait une campagne deux-trois jours avant le long week-end de quatre jours, où l’on sait que les utilisateurs vont souvent retrouver leurs amis, souvent prendre la route. On a fait un push bien contextualisé et ça, c’est la bonne trigger pour proposer la valeur et donner la bonne étincelle à l’utilisateur pour avoir envie d’essayer cette fonctionnalité et la consommer. C’est ce travail-là sur lequel on a à peine commencé, sur lequel on est en train de travailler : proposer Shaker au bon endroit et au bon moment. Typiquement, on est en train de retravailler un peu la façon dont c’est proposé dans l’interface en le mettant plus sur le chemin de la création de playlist, puisque l’un des cas d’usage de Shaker est de créer une playlist avec ses amis pour se mettre d’accord sur quoi écouter. On l’a rajouté dans la tab (=l’onglet) “Favoris”. On l’a mis pas loin du chemin que les utilisateurs empruntent généralement pour créer une playlist pour qu’ils soient conscients de cette fonctionnalité. Ce qui est intéressant quand on travaille sur les expériences chez Deezer, c’est que l’on commence par l’awareness, donc le fait que les utilisateurs sachent que cette fonctionnalité existe. Il est à zéro, donc il y a zéro utilisateur qui connaît cette fonctionnalité, et ça prend grosso modo un an pour arriver à un pourcentage qui permet de débloquer vraiment tous les effets positifs de la fonctionnalité.</p><p><strong>[00:33:23.860] — Benjamin : </strong>Oui, parce que quelque part, ça pourrait remplacer la playlist collaborative, finalement. C’est une playlist collaborative sous stéroïdes qui pourrait s’updater toute seule et qui dit à quel point la chanson est pertinente pour la playlist.</p><p><strong>[00:33:35.110] — Pierre : </strong>Oui, c’est très proche, effectivement. Après, on a une vision autour du Social qui consiste à créer une “open destination”, une destination ouverte qui permet aux utilisateurs de se rencontrer et de profiter de l’expérience amusante et pertinente autour de la musique. À terme, Shaker, je pense que ça fait sens de l’imaginer mélangé avec d’autres fonctionnalités qui sont pertinentes pour les contextes sociaux. Chez Deezer, on travaille aussi sur d’autres fonctionnalités qui sont proches de ce contexte “je suis avec des amis”. Typiquement, les blind tests, c’est une fonctionnalité qui est très proche de Shaker. Aller voir un concert, on le fait souvent avec ses amis, c’est aussi une fonctionnalité qui est très proche, etc. Ça fait partie des prochaines évolutions : commencer par un usage particulier — l’usage le plus récurrent étant la playlist — et en profiter pour amener les utilisateurs à découvrir ensuite d’autres fonctionnalités qui font sens parce qu’ils ont fait l’effort, une fois, de se connecter avec leurs amis et ce serait bien de ne pas leur demander de faire 36 fois cet effort de se connecter. C’est notamment l’un des axes sur lesquels on travaille avec Benjamin maintenant, d’utiliser un peu tous ces signaux sociaux que les utilisateurs laissent dans l’application pour faire émerger ces connexions au bon endroit, au bon moment, créer un peu un “social graph” qui permet de relier les utilisateurs entre eux. Ils commencent dans Shaker et après, ça fait sens que la prochaine fois qu’ils veulent se partager une chanson, on leur propose des utilisateurs de leur Shaker, etc.</p><p><strong>[00:34:59.830] — Vincent : </strong>Si je reviens un peu sur ce que tu disais, il y a eu une recherche aussi de l’efficience, à la fois de la performance par les parcours, par l’optimisation de l’UX, de l’expérience utilisateur. Est-ce qu’il y a eu aussi cette même recherche côté back-end, au sens large, d’essayer d’avoir des choses qui soient très réactives ? Est-ce qu’il y a eu des gros enjeux à ce niveau-là également ?</p><p><strong>[00:35:29.330] — Benjamin : </strong>Oui, j’en parlais un petit peu tout à l’heure, comme il a fallu que l’on passe à un service vraiment online qui réponde… Alors, on avait un peu plus que 400 millisecondes, heureusement, pour pouvoir créer la playlist grâce à des petits hacks d’UX, mais on a dû quasiment réécrire l’algorithme en entier. Il y a eu pas mal d’optimisations dans l’équipe Software Engineer aussi pour pré-charger en mémoire des représentations d’utilisateurs. On s’est basé sur des services qui avaient déjà été créés pour des algos qui doivent répondre vite, comme le Flow ou certains mix dans Deezer. Là en fait, c’est un petit peu comme si l’on cherchait à créer un Flow, mais pour plusieurs personnes en même temps. Donc le but, c’était qu’on passe à l’échelle et que les temps ne soient pas multipliés par le nombre de personnes dans le Shaker. Quelque part, ce que l’on a fait aujourd’hui pourrait supporter beaucoup plus qu’une poignée d’utilisateurs. C’est assez chouette. Mais il y a eu beaucoup d’optimisations, beaucoup de tests, des tests de charges pour s’assurer que tout aille bien. On a d’ailleurs créé une stack séparée pour Shaker afin, déjà, de tester des nouvelles technos qui nous semblaient intéressantes pour cette fonctionnalité-là, et puis pour être sûr aussi de ne pas parasiter la stack existante de la prod actuelle, parce que l’on avait peur que si ça, ça tombait, que ça fasse tomber le reste. Donc on l’a un petit peu mis à part pour s’assurer que tout soit OK. Il y a eu pas mal de défis techniques, quand même, autour de cette fonctionnalité-là. Ça, c’est pour les défis de charges, de calculs. On a aussi eu des défis au niveau de la cohérence de la playlist, parce qu’une fois que l’on a trouvé les chansons à mettre dans la playlist, dans le Shaker, on s’est rendu compte qu’il pouvait y avoir des problèmes de “fairness” en anglais, c’est-à-dire des problèmes d’équitabilité entre les personnes. Il y a des personnes qui vont avoir des gros profils avec 2 000 chansons par exemple, et des personnes qui auront simplement 10, 15, 20, 30 chansons. Et on ne voulait pas que ces ratios soient conservés dans la participation à la playlist. Donc il fallait faire attention à ce que tout le monde soit représenté, tout le monde soit là. Ce sont des choix arbitraires à faire, qui ne sont pas vraiment dictés par l’algorithme en soi. C’est là que ça devient intéressant quand le machine learning Data Science rencontre un peu la réalité du produit et du retour utilisateur. On se rend compte qu’il n’y a pas un algo intelligent qui fait les meilleures choses, mais qu’il faut faire des choix. Il faut que l’humain tranche à un moment donné sur des choses qui sont pré-calculées, pré-optimisées. C’est là qu’on a la possibilité de créer quelque chose d’unique ou de mettre un peu sa patte de product ou de sa marque ou de sa plateforme. Je trouve que c’était la partie la plus intéressante dans le projet, la rencontre technique et humaine.</p><p><strong>[00:38:17.260] — Vincent : </strong>Est-ce que ça veut dire qu’à l’heure actuelle, il y a plusieurs algos selon les cas d’utilisation ? Ou est-ce que l’on a réussi à faire un algo qui cadre à peu près avec l’intégralité des…?</p><p><strong>[00:38:27.290] — Benjamin : </strong>C’est toujours la question de savoir s’il y a plusieurs algos et un algo qui choisit lequel il faut utiliser, ou s’il y en a qu’un qui, finalement, est composé de sous-algos. C’est une manière de jouer sur les mots. Au final, ce qui se passe, c’est que l’on a une stratégie A-B-C-D qui se déclenche en cascade si la première n’a pas marché. Au début, on va tester la chose la plus évidente. On se rend compte que l’on n’a pas assez de chansons alors on va les piocher ailleurs, on va utiliser une autre stratégie. Avec ça, on arrive à peu près à couvrir tous les cas. Ce qui est intéressant aussi dans la fonctionnalité, c’est que l’on essaie d’afficher d’où provient la chanson. On voit directement si une personne est surreprésentée par rapport à l’autre, on va dire : “mais il n’y a que des chansons pour toi !” On essaye forcément de mettre en premier les chansons qui… Si par chance, il y a une chanson qui est adorée par les quatre, cinq, six ou la dizaine d’utilisateurs, elle va être en premier. Petit à petit, on va avoir cette approche décroissante du nombre de personnes pour laquelle cette chanson est intéressante. Et à la fin, on va tout simplement prendre, pas complètement au hasard puisqu’on a toujours un petit peu un ordre de préférence des chansons pour l’utilisateur, mais on s’assure que la playlist soit toujours remplie. Ça, c’est le but.</p><p><strong>[00:39:35.770] — Pauline : </strong>A-t-on un moyen de connaître la satisfaction des membres d’un groupe Shaker vis-à-vis de cet algo ? On leur génère un mix, est-ce que l’on a un moyen d’avoir un retour ou de voir, parce qu’ils ont cliqué à tel endroit, qu’ils sont contents ou qu’ils aimeraient peut-être un autre algo ?</p><p><strong>[00:39:53.890] — Benjamin : </strong>On a toujours des signaux implicites qui concernent l’utilisation de cette playlist. Est-ce qu’elle a été écoutée ? Est-ce qu’elle est réécoutée souvent ? Est-ce que des personnes ont ajouté une chanson en favori qui n’était initialement pas de leur profil ? Ça explique implicitement que l’on a visé juste, que la playlist est bien pour tout le monde. Après, c’est toujours mieux d’avoir des signes vraiment explicites avec un feedback utilisateur.</p><p><strong>[00:40:16.600] — Pierre : </strong>Oui, là-dessus, on travaille avec notre département Recherche Utilisateur à chaque fois que l’on sort une fonctionnalité pour comprendre comment est-ce qu’elle est utilisée quand elle est sortie. On a fait beaucoup d’entretiens avec des utilisateurs pour évaluer la pertinence de l’algorithme, et de façon générale, de la fonctionnalité, pour comprendre si l’on a atteint le but que l’on s’était fixé ou pas. Et on a aussi, dans l’application, souvent des petits sondages qui apparaissent, qui permettent de récolter des signaux qualitatifs ou des feedback qualitatifs sur l’algorithme.</p><p><strong>[00:40:52.120] — Pauline : </strong>Et est-ce que certains de ces feedback ont fait changer de direction certains aspects de Shaker ? Est-ce que l’on a ajouté des choses suite à ces entretiens, ou enlever des choses ?</p><p><strong>[00:41:04.670] — Pierre : </strong>Oui, j’ai déjà parlé de rendre plus explicite l’aspect universel. On a vu que si la proposition de valeur est bien comprise, les utilisateurs qui ne sont pas chez Deezer s’attendent à ce que l’on aille encore plus loin sur l’aspect universel. Ils aimeraient bien pouvoir repartir avec une partie du contenu sur leur app de streaming. C’est justement ce qui va être l’une des prochaines évolutions, d’aller encore plus loin sur l’aspect partageable et donc de pouvoir potentiellement exporter la playlist depuis son Shaker vers son Spotify ou son YouTube ou Apple Music. C’était super intéressant de voir à quel point on pouvait aller encore plus loin.</p><p><strong>[00:41:42.860] — Vincent : </strong>Donc toujours dans la portabilité, toujours se dire que l’on ne garde pas les choses que pour Deezer, on n’essaie pas forcément de faire que pour accaparer des utilisateurs que l’on essaie de garder captifs, mais on essaie de garder ce partage.</p><p><strong>[00:41:57.270] — Pierre : </strong>Exactement. Toute l’équipe a fait un chemin conceptuel pour changer d’avis là-dessus. C’est vrai que c’est un peu contre-intuitif, comme le sous-entendait votre question en début d’émission.</p><p><strong>[00:42:15.930] — Vincent : </strong>Juste par curiosité, est-ce que l’on a une idée de la taille des groupes dans Shaker ? Est-ce que c’est quatre-cinq ? Est-ce que c’est plus la dizaine, ou je ne sais pas, la centaine ? Soyons fous !</p><p><strong>[00:42:25.810] — Pierre : </strong>Je n’ai plus la moyenne à la décimale près, mais c’est autour de quatre personnes par groupe exactement.</p><p><strong>[00:42:29.800] — Vincent : </strong>D’accord. Donc, ça reste quand même relativement gérable d’un point de vue humain. On n’est pas sur des…</p><p><strong>[00:42:34.390] — Pierre : </strong>Non, la base, le concept derrière, c’est d’aborder des micro-communautés, donc des communautés qui existent déjà — on va dire, la garde rapprochée de chaque utilisateur. D’ailleurs, Shaker ne peut, pour le moment, être utilisé que jusqu’à quinze. Mais ce qui est intéressant, justement, c’est que l’on a vu que la proposition de valeur autour de l’éditorialisation des goûts des personnes résonne aussi sur des plus grandes audiences. On a notamment testé Shaker avec des artistes, entre artistes et fans, et on sent de l’appétence autour de cet angle-là. Donc c’est aussi quelque chose sur lequel on va travailler.</p><p><strong>[00:43:08.060] — Pauline : </strong>Donc des utilisateurs ont pu créer un groupe Shaker avec un artiste ?</p><p><strong>[00:43:13.050] — Pierre : </strong>Tout à fait. On a eu quelques artistes qui ont permis à leur communauté de faire des Shaker, avec leurs plus grands fans. Pour la petite histoire, l’artiste Pomme, qui est chez Apple Music, a fait un Shaker avec elle-même lors d’un événement que l’on a organisé récemment — les événements “Purple Door”, qui permettent aux plus grands fans d’un artiste d’assister à une représentation de cet artiste dans un concert privé. On a testé avec plusieurs artistes différents. L’identité musicale de quelqu’un est une information assez inoffensive pour créer de la proximité entre des utilisateurs qui ne sont pas forcément proches. C’est super pour les fans de pouvoir se dire : “j’ai 70% de compatibilité avec Gazo”, “tiens, Gazo, il écoute ça aussi”. C’est vraiment l’aspect plus émotionnel de la musique qui est intéressant.</p><p><strong>[00:44:05.220] — Vincent : </strong>Je m’imaginais aussi, pourquoi pas… Tu disais que c’est limité, à l’heure actuelle, à quinze personnes. On pourrait imaginer des classes dans des lycées ou des clubs de je-ne-sais-quoi qui voudraient voir leur compatibilité, donc des groupes peut-être plus importants un jour ?</p><p><strong>[00:44:22.160] — Pierre : </strong>Tout à fait. Il fallait choisir au début. Et il y a aussi des challenges d’un point de vue recommandation : ça devient plus difficile de faire une playlist pertinente pour cent personnes versus quatre ou quinze personnes. Donc on s’est vraiment concentré, au début, sur ce cas d’usage : les amis ensemble. Mais effectivement, on est déjà en train de ré-imaginer certaines parties de la proposition de valeur de Shaker, utilisées ailleurs pour les rendre plus faciles à consommer dans des contextes particuliers et pour permettre aussi d’élargir un peu l’audience dans un groupe. Autre contexte qui est pertinent pour les amis et la musique, c’est les festivals. Deezer, chaque été depuis l’été 2023, propose un booth (=un stand) avec un concept très proche de Shaker, où vous venez avec vos amis, vous indiquez quels sont vos goûts musicaux, vos préférences musicales, et vous avez droit à un DJ set privé dans un espace fermé avec une playlist qui est construite spécifiquement pour ces goûts musicaux. C’est effectivement très pertinent dans ce genre de contexte.</p><p><strong>[00:45:21.220] — Vincent : </strong>On a déjà parlé de mix avec un artiste, on vient de parler de festivals. Est-ce qu’il y a d’autres choses qui pourraient venir enrichir l’expérience Shaker dans les semaines/mois à venir ?</p><p><strong>[00:45:33.250] — Pierre : </strong>Oui, on travaille beaucoup plus sur l’aspect “lean back” de consommation de cette fonctionnalité. Dans la musique, il y a deux usages : “lean forward” et “lean back”. “Lean forward”, c’est : je regarde mon écran et j’ai des actions explicites, intentionnelles avec l’interface de l’app de musique. Et il y a l’aspect “lean back” : j’ai mis le téléphone dans ma poche et je consomme, de façon un peu plus passive, la musique. On s’est rendu compte que c’est intéressant de travailler un peu sur les notifications autour de Shaker, donc de faire un peu plus de notifications autour de l’usage, qu’est-ce qui se passe dans le groupe lorsque l’on a rejoint un groupe, parce que c’est une interaction qui va se faire… Souvent, ça commence quand les gens sont ensemble, ils vont faire la même chose, au même endroit, au même moment ou dans un laps de temps assez rapproché. Après, la playlist ou la façon de se partager des chansons, ça va être fait de façon un peu plus asynchrone, chacun l’un après l’autre, avec des laps de temps plus ou moins différents. On a un peu travaillé sur cet aspect-là en travaillant avec notre équipe CRM pour améliorer la consommation de la fonctionnalité. Donc vous êtes notifiés un peu plus souvent si quelqu’un rajoute une chanson dans le groupe. Un autre angle aussi que l’on voulait explorer, c’est valoriser les utilisateurs qui font l’effort de partager beaucoup de chansons dans leur Shaker en leur faisant savoir si les utilisateurs de leur groupe qui ont écouté la chanson l’ont aimée. Donc on va rajouter une notification qui vous informe si la chanson que vous avez rajoutée a été aimée par d’autres membres du groupe. Pour créer cette boucle positive où vous faites un petit effort pour rajouter une chanson et vous êtes récompensé parce que vous savez si vos amis ont écouté cette musique et l’ont aimée. C’est intéressant parce qu’au tout début du projet, on a regardé un peu la littérature scientifique sur les playlists collaboratives et il y avait des “pain points”, des problèmes utilisateurs, qui étaient souvent remontés, c’est que quand on fait une playlist collaborative, souvent, il y a un membre du groupe qui va prendre l’ascendant sur tout le monde et rajouter beaucoup de chansons, ou en fait, les gens rajoutent beaucoup de chansons au début, puis après, s’arrêtent. Or, il y a un intérêt à se partager des choses de façon assez organique. On découvre souvent de la musique, on a souvent envie de la partager avec ses amis. C’est cet angle-là sur lequel on travaille. Et puis, comme je vous l’ai dit, on travaille sur l’aspect un peu plus universel en allant encore un peu plus loin sur la possibilité de consommer la valeur, même si on n’est pas chez Deezer, pour que ce soit encore plus puissant. Et le troisième axe, ça va être de faire le pont entre cette proposition de valeur autour du partage de musique avec d’autres propositions de valeur qui sont très proches et qui peuvent faire sens à certains moments, comme des fonctionnalités qui permettent de s’amuser, notamment les blind tests.</p><p><strong>[00:48:10.770] — Benjamin : </strong>D’un point de vue algo, on a toujours le développement continu de l’algorithme, faire en sorte qu’il soit de plus en plus précis en se basant peut-être sur des A/B tests ou sur des retours utilisateurs, que ce soit de la User Research ou directement la satisfaction implicite via le stream, le temps passé sur la fonctionnalité. On pensait peut-être aussi incorporer les moods. Tout simplement, on peut avoir une playlist et se dire : “OK, on a mis toutes nos chansons mais si on pouvait ne garder que ce qui est OK pour une soirée, ou pour un voyage en voiture, ou pour se détendre, ou un truc tranquille pour bosser ensemble, ce serait, je pense, quelque chose qui serait intéressant”. Et puis, on commence aussi à voir des fonctionnalités de Shaker qui reviennent dans l’app principale — puisque Shaker a été développé un petit peu à côté dans un premier temps. Maintenant, quand on va sur une page profil de n’importe quelle personne, on peut avoir instantanément l’affinité musicale ou la compatibilité musicale qui s’affiche. Et on peut voir — ça réutilise un peu des composants de Shaker d’ailleurs — quelles sont les chansons de la librairie de cette personne qui pourraient me plaire le plus. Parce que parfois, la personne va avoir 4 000 chansons avec plein de genres confondus, mais dans ce que j’écoute, moi, est-ce que l’on a des trucs en commun ? Est-ce qu’il y a des choses qui pourraient m’intéresser ? On essaie de rendre les pages profil un peu plus intelligentes, que ce ne soit pas juste un exposé plat de ce que la personne écoute, avec le prisme de la préférence musicale de la personne qui visite ce profil, et peut-être même, je crois, dans le futur, la possibilité de faire instantanément un Shaker avec cette personne-là, si le score est assez élevé. Je pense que Shaker est un bon moyen d’engager avec d’autres personnes et de redonner une dimension un peu sociale, comme le disait Pierre tout à l’heure, dans l’app, ce que Deezer avait un petit peu au début de son histoire — on pouvait mettre des commentaires sous les albums, on pouvait mettre des commentaires sous les chansons, sous les playlists, ça créait du lien. C’est un peu comme quand on est sur YouTube, on écoute une chanson et on se rend compte qu’il y a 400 personnes qui ont mis un commentaire et on se sent moins seul, on a l’impression de l’écouter à plusieurs. Je pense que Shaker peut répondre un peu à ce problème aussi.</p><p><strong>[00:50:11.760] — Vincent : </strong>Merci beaucoup, en tout cas, pour toutes ces infos.</p><p><strong>[00:50:13.870] — Pauline : </strong>On a hâte de voir ça !</p><p><strong>[00:50:17.280] — Vincent : </strong>Clairement. On a hâte d’essayer !</p><p><strong>[00:50:17.750] — Benjamin : </strong>Merci à vous !</p><p><strong>[00:50:17.280] — Pierre : </strong>N’hésitez pas : shaker.deezer.com.</p><p><strong>[00:50:19.470] — Vincent : </strong>Et on va passer à la deuxième partie de l’émission, qui est vos coups de cœur musicaux.</p><p><strong>[00:50:35.040] — Pauline : </strong>Quels sont vos coups de cœur musicaux du moment ou absolus, de tous les temps ?</p><p><strong>[00:50:41.410] — Pierre : </strong>Moi, je voudrais recommander un album qui est sorti en 2023, qui s’appelle “Principia”. C’est un album de rock indépendant d’un groupe qui s’appelle En Attendant Ana. C’est un groupe français. Chose rare, cet album a été listé par le Time comme l’un des 100 albums les plus importants de 2023. Je vous recommande chaudement cet album qui est, je crois, le troisième album du groupe et l’album le plus abouti.</p><p><strong>[00:51:08.880] — Benjamin : </strong>Moi, j’écoute plutôt en boucle, depuis que c’est sorti, un single de Fontaines D.C. qui s’appelle Starburster. Ce n’est pas un groupe que j’écoutais énormément avant. J’en avais entendu un peu parler, j’avais écouté deux-trois chansons, mais là, j’ai été happé par ce single, donc j’ai hâte que l’album sorte.</p><p><strong>[00:51:24.460] — Vincent : </strong>Moi, s’il faut que j’en choisisse un… C’est très compliqué ! Alors, on va se dire un titre que j’ai écouté, qui date d’il y a un moment maintenant et que j’avais découvert chez Bernard Lenoir il y a des années et des années, qui s’appelle “Ruins”, d’un groupe qui s’appelle O. Children. J’ai adoré ce titre. Le chanteur avait une texture de voix un peu comme Ian Curtis de Joy Division. Voilà, si je devais en recommander un ce mois-ci, ce sera ça. Et Pauline ?</p><p><strong>[00:51:54.240] — Pauline : </strong>Moi, je vais vous recommander une artiste norvégienne qui s’appelle Dagny. C’est de la pop indé. Elle vient de sortir son deuxième album, il me semble. Je ne l’ai pas encore écouté, mais c’est assez dansant, c’est frais, et je pense que c’est pas mal pour l’été.</p><p><strong>[00:52:10.600] — Vincent : </strong>Et voilà qui conclut ce dernier épisode de la saison !</p><p><strong>[00:52:13.390] — Pauline : </strong>Merci d’avoir participé à ce grand final !</p><p><strong>[00:52:17.150] — Pierre : </strong>Merci beaucoup.</p><p><strong>[00:52:17.780] — Benjamin : </strong>Avec plaisir, on est honoré !</p><p><strong>[00:52:20.960] — Pauline : </strong>Et peut-être à une prochaine !</p><p><strong>[00:52:22.570] — Vincent : </strong>Et voilà ! Stay tuned !</p><p><strong>[00:52:26.300] — Vincent : </strong>Vous venez d’écouter un épisode de Deez is la tech et nous espérons que vous avez passé un bon moment en notre compagnie. N’hésitez pas à nous attribuer quelques étoiles si votre application de podcast le permet, et à nous faire part de vos retours via les réseaux sociaux et notre compte @DeezerDevs. Ceux-ci nous aideront à améliorer notre contenu afin de le rendre plus utile, enrichissant et plaisant à écouter. Enfin, n’oubliez pas que toutes les transcriptions de nos épisodes, ainsi que les coups de cœur de nos invités, sont disponibles sur notre blog deezer.io. À très vite pour un nouvel épisode et d’ici là, ne pétez ni les plombs, ni les crons !</p><h3>Références</h3><ul><li><a href="https://www.deezer.com/explore/fr/features/shaker/">Shaker : le mix qui vous rassemble</a><br>Pour essayer la fonctionnalité : <a href="https://shaker.deezer.com/">https://shaker.deezer.com</a></li><li><a href="https://www.apple.com/fr/siri/">Siri</a> : l’assistant intelligent d’Apple</li><li>Apple. (2024, 10 juin). Communiqué de presse du 10 juin 2024 : <em>Voici Apple Intelligence, le système d’intelligence personnelle qui place des modèles génératifs puissants au cœur de l’iPhone, de l’iPad et du Mac</em>. <a href="https://www.apple.com/fr/newsroom/2024/06/introducing-apple-intelligence-for-iphone-ipad-and-mac/">https://www.apple.com/fr/newsroom/2024/06/introducing-apple-intelligence-for-iphone-ipad-and-mac/</a></li><li><a href="https://www.tunemymusic.com/fr">TuneMyMusic</a> : transférez vos playlist et favoris entre les services musicaux</li><li><a href="https://www.deezer.com/explore/fr/features/flow/">Flow</a>, ton mix infini et personnalisé</li><li>Bellenger, A. pour Deezer. (2024, 9 avril). Communiqué de presse du 9 avril 2024 : <em>Purple Door, l’expérience incontournable de Deezer, réunissant fans et artistes, revient avec une performance musicale inédite de l’artiste Pomme</em>. <a href="https://newsroom-deezer.com/fr/2024/04/purple-door-lexperience-incontournable-de-deezer-reunissant-fans-et-artistes-revient-avec-une-performance-musicale-inedite-de-lartiste-pomme/">https://newsroom-deezer.com/fr/2024/04/purple-door-lexperience-incontournable-de-deezer-reunissant-fans-et-artistes-revient-avec-une-performance-musicale-inedite-de-lartiste-pomme/</a></li><li><a href="https://www.radiofrance.fr/personnes/bernard-lenoir">Bernard Lenoir</a> : producteur de radio et de télévision</li><li>Coups de coeur :<br>- “<a href="https://deezer.page.link/piFJnqg78ktZp7LM7">Principia</a>” de En attendant Ana<br>- <em>“</em><a href="https://deezer.page.link/n4VcVJeSGy5tThb56"><em>Starburster</em></a><em>”</em> de Fontaines D.C.<br>- <em>“</em><a href="https://deezer.page.link/QufJfwGKELnH8wEv9"><em>Ruins</em></a><em>”</em> de O. Children<br>- <a href="https://deezer.page.link/jPvBzmBwtCEN64XY8">Dagny</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cf844f4ae890" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/dilt-s02e07-shaker-ou-la-cr%C3%A9ation-dune-nouvelle-exp%C3%A9rience-collaborative-anatomie-d-une-cf844f4ae890">DILT — S02E07 — Shaker ou la création d’une nouvelle expérience collaborative — Anatomie d’une…</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Road to FOSDEM 2024]]></title>
            <link>https://medium.com/deezer-engineering/road-to-fosdem-2024-11d3084cfd70?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/11d3084cfd70</guid>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[fosdem]]></category>
            <category><![CDATA[conference]]></category>
            <category><![CDATA[open-source]]></category>
            <dc:creator><![CDATA[Alexis FALA]]></dc:creator>
            <pubDate>Wed, 05 Jun 2024 08:18:32 GMT</pubDate>
            <atom:updated>2024-06-05T09:01:44.737Z</atom:updated>
            <content:encoded><![CDATA[<p><em>co-authored by </em><a href="https://medium.com/u/1df55d0db6c5"><em>Denis GERMAIN,</em></a><em> </em><a href="https://medium.com/u/7aeaeaa646d8"><em>Armelle Bengochea</em></a><em>, </em><a href="https://medium.com/u/23f7fa4f2a45"><em>Benjamin Martin</em></a><em>, </em><a href="https://medium.com/u/50c62325cb31"><em>Weeking</em></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qldxB5s0hS7-n7VI" /></figure><h3>What is FOSDEM ?</h3><p>We had the opportunity to attend FOSDEM once again this year. If you’re not familiar with it, FOSDEM stands for <em>Free and Open-source Software Developer’s European Meeting</em>.</p><p>FOSDEM is one of the largest European conferences and it takes place every year at the Université Libre de Bruxelles (The Free University of Brussels) in Belgium.</p><p>This event is always one of our favorites to attend! Why? Because there are nearly 60 different tracks and 855 talks, and so many different communities coming together for two days in the same place!</p><p>Don’t worry if you missed it, we’ve prepared a recap of the talks that stood out the most for us!</p><h3>Our FOSDEM Survival Guide</h3><p>There were over 8,000 people there this year, all sharing the same passion, so it’s a necessity to come prepared! Whether you’re a returning veteran or attending for the first time, here are our FOSDEM survival tips:</p><ol><li>The number one rule of FOSDEM is: you won’t be able to see all the talks that interest you. Don’t get frustrated, the conferences will be available for replay (and even live if you don’t have time to change rooms!)</li><li>Interested in a talk? Get ahead by trying to enter the room before the previous session is over. This way, you’ll surely get a comfortable seat while avoiding the crowds as much as possible.</li><li>If you want to eat at the food trucks while avoiding the crowd, steer clear of the 12pm-2pm slot.</li><li>You’ll find the majority of goodies in building K :)</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*iV-VmoMrW_HeHWZS" /></figure><h3>Our selection of talks</h3><h4>Modern application observability with Grafana and Quickwit</h4><p>By <a href="https://fosdem.org/2024/schedule/speaker/7AKMRU/">François Massot</a> — <a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3514-modern-application-observability-with-grafana-and-quickwit/">Video &amp; slides</a></p><p>Before attending this wonderful talk presented by François Massot, the co-founder of QuickWit and one of the core developers of the QuickWit engine (Rust), we were expecting to see yet another new Search Engine. As it turns out, we were pleasantly surprised by QuickWit’s potential.</p><p>But first, let us introduce you to the observability of modern context. We live in a world of SOA (Service-Oriented Architecture) and distributed systems, and we have access to different telemetry data sources like metrics, logs and traces. OpenTelemetry showed up as a standard with SDKs and tools to be able to instrument, generate, export and collect these telemetry signals, which enabled new observability solutions. QuickWit is one of them.</p><p>We will start by explaining a bit what QuickWit is. It’s a powerful search engine based on a Rust library named Tantivy for full-text search — both projects are maintained by the QuickWit team. The solution supports OTEL-native traces and logs and the people behind it told us about their dream to support metrics (spoiler alert, they’re currently working on it). QuickWit is compatible with the ElasticSearch API, meaning an ES client can take advantage of the solution, like fluent-bit and its ES output plugin. Trust us, it’s not just another search engine, because QuickWit (written in Rust) consumes less CPU/memory for the same job and does it faster than ES or OpenSearch, particularly on large data models. Also, it’s schemaless, so you can put any document on it, and it’s Kubernetes ready with a helm chart. For the storage part, they use s3 backend with indexing, so it ensures a really low latency request, same as if it’s remote object storage. And obviously, they have a Grafana Datasource plugin, heavily inspired by the ElasticSearch plugin.</p><p>Now that you better understand the potential of Quickwit, let’s jump to François’ talk. He started by explaining the observability challenges in modern contexts and gave us details about the specific storage backend types currently used for the traces, logs and metrics. He invited us to pay attention to cardinality, simply because traces and metrics can both be stored on TSDB backend type, but traces are not adapted to it. Why? Two reasons:</p><ul><li>TSDB is optimised for monitoring and suffers from high cardinality. So you need to control them to avoid hugely increasing your compute and storage costs.</li><li>The Trace telemetry data is not made to be controlled, label filtered or thinned down. You need to keep everything and -understand the full context with every information available, like userID, requestID, details, etc.</li></ul><p>François wanted to explain to us why Grafana Tempo, QuickWit and other distributed tracing backend exist and why they’re better than using a classical TSDB backend.</p><p>Then François described to us why QuickWit, a search engine, is a great solution as a trace and logs backend too. The main reasons are the needs around traces. We generate a lot of them, they contain a lot of data, we need to query them often, and we need to have low latency queries through billions of spans. All of that means it can be hard to scale the storage and to have low latency requests. That’s exactly where QuickWit comes in. It uses s3 storage backend being scalable and resilient, it uses the indexing to control the request latency (under 1 second same for large data model) and it uses a decoupled storage and compute architecture allowing us to independently scale searchers and indexers.</p><p>Now, I’ll try to explain how a remote object storage with indexing provides low latency responses on very large data models based on the details François gave us. This the QuickWit architecture schema François used in his slides:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*l_2vgBD8Wwi_byWc" /></figure><p>To the left, we can see the write path in QuickWit’s architecture with indexing, and to the right, we can see the read path with the search part. With an interval in seconds, QuickWit builds and pushes to the object storage a bunch of splits containing docs data and metadata. Then, for each split, a line is added to the metastore file containing the split metadata, therefore the searcher can access it. Now, let’s talk about split details:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ExH77EKFOALc5GWD" /></figure><p>A split contains three data structures:</p><ul><li>The Doc store: It’s a raw-oriented storage part waiting to receive a DocumentID in order to provide the full document content.</li><li>The Inverted index: If you provide a field like a userID, it pops a list of DocumentID containing this userID.</li><li>The Columnar store: It’s used for aggregation and analytics.</li></ul><p>And it also stores the three data structures metadata in a hotcache called “footer split”. This hotcache has been optimised to always be in the searchers’ cache. And with the pointers to the data structures, it allows searchers to find data in one or two requests maximum. That’s how QuickWit is optimised for searching logs and traces with object storage.</p><p>After the QuickWit technical presentation, François moved on to a demo of how it stores spans based on the OTEL data model. We said it’s schemaless, so you can store anything you want, but for spans you really want to follow the OTEL standard. François generated a lot of spans and traces in order to show how he deals with application monitoring and QuickWit. He used a Grafana labs project named “xk6” allowing him to build k6 with extensions, and he used the tracegen Golang package to generate traces easily. He deployed a QuickWit cluster on a Kubernetes cluster and created a Grafana instance on Kubernetes.</p><p>Firstly, he demonstrated the QuickWit Web UI that can list indexes, check their details, check cluster details and display node information. He showed us how much QuickWit is compressing files because splits were around 40GB while the size of uncompressed published docs was 290GB. And yes, QuickWit does compaction too, it explains all the panel’s CPU spikes. It merges splits. François put more than 400M of spans into the otel-trace index and kept sending around 15k docs/s in live (~1TB per day of data).</p><p>He used the Grafana explore tab to discuss with QuickWit (Lucene query style like ES), and with inverted index, he showed us how you can search and navigate easily into spans. It was very fast. Then he demonstrated how to dig into one specific trace with the explore Jaeger plugin targeting the QuickWit cluster. It works very well and returns all spans for a given trace_id. Here is the debug view from Grafana:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/760/0*Qc-UUNfEAK47YcCc" /><figcaption>Just for your information, a trace can be identified as a group of spans forming a complete request debug view. It can be a distributed trace if the trace travels to several services.</figcaption></figure><p>Unfortunately, François didn’t have enough time for questions because of a little technical issue at the beginning of the talk. He would have liked to further explain the debugging way with Grafana and QuickWit, but we think he did an excellent job. He managed to hold our attention and motivated us to do a “Quick QuickWit Proof of Concept”.</p><p>If we had to pick out a few points to remember, here’s what they would be:</p><ul><li>observability, thanks to the OpenTelemetry standard, is growing up and is promising for the future. We will have new very good solutions and QuickWit is the proof.</li><li>traces and spans are nowadays mandatory in SOA to be able to debug failures, crashes and errors.</li><li>QuickWit is a great tool to store logs and traces for large amounts of data. Thanks to the indexing, the indexers/searchers scalability and the s3 storage, it seems to be amazing and seems to be very easy to maintain.</li><li>QuickWit comes with a Kubernetes ready architecture, a very efficient and performant search engine (written in Rust) and a good hype, which makes us strongly believe in the project.</li></ul><p><strong>Additional resources</strong>:</p><ul><li>QuickWit website: <a href="https://quickwit.io/docs/overview/introduction">https://quickwit.io/docs/overview/introduction</a></li><li>QuickWit GitHub organization: <a href="https://github.com/quickwit-oss">https://github.com/quickwit-oss</a></li><li>QuickWit search engine source code: <a href="https://github.com/quickwit-oss/quickwit">https://github.com/quickwit-oss/quickwit</a></li><li>Tantivy source code: <a href="https://github.com/quickwit-oss/tantivy">https://github.com/quickwit-oss/tantivy</a></li><li>QuickWit Grafana Datasource source code: <a href="https://github.com/quickwit-oss/quickwit-datasource">https://github.com/quickwit-oss/quickwit-datasource</a></li><li>QuickWit helm chart: <a href="https://github.com/quickwit-oss/helm-charts">https://github.com/quickwit-oss/helm-charts</a></li><li>xk6 source code: <a href="https://github.com/grafana/xk6">https://github.com/grafana/xk6</a></li><li>tracegen Golang package: <a href="https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/tracegen">https://pkg.go.dev/github.com/open-telemetry/opentelemetry-collector-contrib/tracegen</a></li></ul><h4>Multithreading and other developments in the ffmpeg transcoder</h4><p>By <a href="https://fosdem.org/2024/schedule/speaker/HRBWHZ/">Anton Khirnov</a> — <a href="https://fosdem.org/2024/schedule/event/fosdem-2024-2423-multithreading-and-other-developments-in-the-ffmpeg-transcoder/">Video &amp; slides</a></p><p>In this insightful talk, Anton Khirnov, one of the key developers of ffmpeg and the main architect of the ffmpeg transcoder, explained how he and his team refactored the whole transcoder codebase for the upcoming ffmpeg version 7. We found this talk to be an excellent example of dealing with a daunting technical debt, and trying to rewrite a significant codebase according to a modular and incremental approach.</p><p>To better comprehend the extent of the changes, let’s first talk about the ffmeg transcoder, i.e. the command-line tool.</p><p>Ffmpeg is arguably the most used multimedia transcoding pipeline. Virtually almost any software that involves audio or video content somehow depends on it, including an extensive use here at Deezer. Technically, a typical modern ffmpeg process can be summarised with the following graph:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*k2XNTZgPxpg1TwOd" /></figure><p>From a set of input multimedia streams or files (audio and/or video, represented in green), decoders are applied, then filters apply effects or transformations (in yellow). The outputs are then fed through encoders (in blue), before being multiplexed into output streams or files (in red). Connections can be skipped, split to multiple separate blocks or fed from multiple sources, which makes transcoding rather complex <a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">DAGs</a>.</p><p>As Anton described, open source contributions to ffmpeg have flowed relentlessly over the last 20+ years from a significant number of distinct contributors. Yet, each contribution was always made with the same objective in mind: minimise both the code diff and the amount of work necessary to add, fix or review product features. The overall design or the ease of future development, however, were not a key part of the contribution process. Moreover, the fact that the number of features exploded each year led to a “ridiculous number of options that absolutely none, even developers, can remember”, as Anton confirmed.</p><p>For these reasons, the ffmpeg transcoder suffered from a substantial technical debt, with many complex yet critical parts of code very hard to comprehend and maintain.</p><p>To tackle this problem, Khirnov decided to rewrite the project in an oriented object paradigm, with the strong objective of making the codebase look like the graph presented above, as much as possible at least. On top of a huge refactoring task that also included “removing old things that didn’t work and nobody could understand”, Khirnov said. The general methodology: list all instances and their dependencies, clean each one of them, add isolation to components while respecting the graph object design as above. This massive refactor was reportedly the most complex in decades, through a series of 700+ patch sets spanning two years of work.</p><p>Besides redesigning the transcoding pipeline, Khirnov leveraged the new modular system to isolate components and make them runnable in parallel. Other advantages of modularity include predictable, deterministic outputs, as well as better probing capabilities, for latency estimation in particular. All of these make for new key major features for the transcoding tool, as byproducts of a new cleaner architecture.</p><p>We found this talk particularly inspiring as a great example of how the architecture of a complex, entangled and heavily patched piece of software can be entirely redesigned, no matter how popular and critical it is, with a systematic and methodical approach. A significant effort, but that brings key outcomes: not only the resulting codebase is much easier to maintain and to support future developments, but it also yields new features critical to the application itself.</p><h4><em>Putting an end to Makefiles in Go projects with GoReleaser</em></h4><p>By <a href="https://fosdem.org/2024/schedule/speaker/DSNJKU/">Denis Germain</a> — <a href="https://fosdem.org/2024/schedule/event/fosdem-2024-1853-putting-an-end-to-makefiles-in-go-projects-with-goreleaser/">Video &amp; slides</a></p><p>For the first time in his career, Deezer’s Senior Lead SRE Denis Germain had the opportunity to present a talk at FOSDEM, in the Golang devroom.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*-xrvHHo70g4a1-yl" /></figure><p>The presentation was on GoReleaser, a tool built in Golang that aims to automate and ease all tedious tasks when releasing Golang software. Most of the talk was actually live coded.</p><p>Despite a minor glitch during the demo, he showed us that automating the whole delivery of a new release of a Golang app (cross building the binary, creating and pushing a docker image, adding a release in GitLab with archives and checksums, sending a Mastodon toot) could be as easy as writing a few lines of YAML code, under 15 minutes time.</p><p>Even though most enjoyed this beginner-friendly talk, FOSDEM’s Golang devroom is generally used for more advanced topics, which led to some comments at the end of the presentation. We promise we will bring more expert material next time <strong>😉.</strong></p><h3>An idea for improvement</h3><p>While this event is very enjoyable and informative (although tiring), there’s certainly room for improvement.</p><p>In particular, the level of knowledge recommended for understanding each talk is not clearly communicated. We felt that some were either too advanced, or not advanced enough. We think adding information about the intended audience level for each talk would help attendees make the most of their FOSDEM experience in the future!</p><h3>“All I’m askin’…is for a little respect,” says Aretha</h3><p>FOSDEM is an annual event that only takes place thanks to the participation of many volunteers. It’s a free event, intended to be non-commercial, which in our humble opinion, embodies what open-source is by definition: the free sharing of knowledge without barriers.</p><p>However, in their race to make the most out of the weekend, it seems some participants forgot all manners. In some devrooms, in particular the Containers track, which is one of the most visited tracks of the event, Q&amp;A sessions had to be shortened because people were rushing out as soon as the talk had ended, while speaking out loud. This type of incivility surely impacted other participants’ experience in a negative way, which is a shame.</p><p>It’s worth reminding that speakers also take part voluntarily, with some traveling thousands of kilometers to attend the conference, which makes FOSDEM one of the biggest tech events in Europe. So out of respect for them and the audience, it would seem reasonable to expect that all attendees exit (and enter) rooms as quietly as possible when a talk is ongoing.</p><h3>Conclusion</h3><p>Once again this year, FOSDEM was rich in experiences, inspiring talks, and of course, waffles. We hope to participate again next year!</p><p>Until then, we would like to thank all organizers and volunteers for allowing us to spend two wonderful days in Brussels!</p><p>Keep up the great work to ensure that FOSDEM remains an exceptional event that all of our teams eagerly await 🙂</p><figure><img alt="Thanks Firefox for this joke !" src="https://cdn-images-1.medium.com/max/1024/1*dWMv9JRCi0Di55OVDZBN5A.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=11d3084cfd70" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/road-to-fosdem-2024-11d3084cfd70">Road to FOSDEM 2024</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DILT — S02E06 — Good COP, bad COP : comment organiser et animer une Communauté de Pratique]]></title>
            <link>https://medium.com/deezer-engineering/dilt-s02e06-good-cop-bad-cop-comment-organiser-et-animer-une-communaut%C3%A9-de-pratique-dea6047126fa?source=rss----2ab7e077175a---4</link>
            <guid isPermaLink="false">https://medium.com/p/dea6047126fa</guid>
            <category><![CDATA[knowledge-sharing]]></category>
            <category><![CDATA[knowledge-management]]></category>
            <category><![CDATA[deezer]]></category>
            <category><![CDATA[communities-of-practice]]></category>
            <category><![CDATA[deez-is-la-tech]]></category>
            <dc:creator><![CDATA[Pauline Munier]]></dc:creator>
            <pubDate>Wed, 22 May 2024 09:59:02 GMT</pubDate>
            <atom:updated>2024-05-22T13:29:24.206Z</atom:updated>
            <content:encoded><![CDATA[<h3>Deez is la tech — S02E06 — Good COP, bad COP : comment organiser et animer une Communauté de Pratique</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5wZ2bnFEXv87JgVl49KMTg.png" /></figure><p>Après avoir <a href="https://deezer.io/deez-is-la-tech-s02e02-en-t%C3%AAte-%C3%A0-t%C3%AAte-avec-des-sre-missions-quotidien-et-challenges-7a45e4bdd1f3">démystifié le sigle SRE</a> il y a quelques mois, notre équipe s’attaque maintenant aux COP. Si l’acronyme a été rendu célèbre par la COP21 ou encore la COP27, il n’est pas question d’environnement ici, mais de “communautés de pratique” (“<strong>C</strong>ommunities <strong>O</strong>f <strong>P</strong>ractice” en anglais).</p><p>Théorisé dans les années 90, le concept de communauté de pratique s’est, depuis, peu à peu propagé en entreprise, en particulier dans le milieu de la tech où les COP apparaissent désormais comme des entités incontournables en termes de gestion des savoirs, de partage de connaissances et d’apprentissage continu.</p><p>Mais alors, comment créer, organiser et faire vivre une COP ? Nos trois invités partagent leurs retours d’expérience et bonnes pratiques de leaders de COP dans ce nouvel épisode du podcast Deez is la tech.</p><p><em>Note: This post accompanies the release of the sixth episode of the second season of “Deez is la tech”, a podcast created by Deezer’s Product &amp; Tech teams — in French only for now. You can still find English content on </em><a href="https://deezer.io/"><em>deezer.io</em></a><em> though. Go check it out!</em></p><h3>Résumé de l’épisode</h3><p>Nouvel épisode, nouvel acronyme à décrypter ! Loïc Doubinine (<a href="https://twitter.com/Ztec6/">X</a> | <a href="https://www.linkedin.com/in/zedteque/">LinkedIn</a>) et Vincent Lepot (<a href="https://twitter.com/neozibok">X</a> | <a href="https://www.linkedin.com/in/vincent-lepot-3013625/">LinkedIn</a>) s’intéressent aujourd’hui aux COP, pour Communities Of Practice, ou Communautés de Pratique en français.</p><p>Qu’est-ce qu’une COP ? Comment fonctionnent-elles ? Comment les animer et maintenir l’intérêt de leurs membres, notamment sur le long terme ? Comment prendre des décisions et assurer la participation de chacun ? Et enfin, comment combiner initiatives issues des COP et priorités des équipes ?</p><p><a href="https://medium.com/u/1df55d0db6c5">Denis GERMAIN</a> (Site Reliability Engineer — <a href="https://twitter.com/zwindler">X</a> | <a href="https://www.linkedin.com/in/denis-germain/">LinkedIn</a>), Souhail Jebali (Engineering Manager — <a href="https://www.linkedin.com/in/souhail-j-296b4744/">LinkedIn</a>) et Nicolas Morellet (QA Expert — <a href="https://www.linkedin.com/in/nicolas-morellet/">LinkedIn</a>) partagent leurs expériences respectives de leads de COP, semées de succès et d’embûches, et prodiguent quelques conseils à quiconque souhaiterait créer ou participer à une communauté de pratique.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fembed.acast.com%2F632c77d9a2a9bf001245558b%2F664c9f7fef9f82001215108e&amp;display_name=Acast&amp;url=https%3A%2F%2Fembed.acast.com%2F632c77d9a2a9bf001245558b%2F664c9f7fef9f82001215108e&amp;image=https%3A%2F%2Fthumborcdn.acast.com%2FOMmhur2DGRIb32EV4Mj2z_n1JFA%3D%2F350x350%2Fhttps%253A%252F%252Fassets.pippa.io%252Fshows%252F632c77d9a2a9bf001245558b%252F1705427540204-371d6018382bd843f94dbfbb28c9290e.jpeg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=acast" width="600" height="180" frameborder="0" scrolling="no"><a href="https://medium.com/media/1b745d87344b6acea5ba911a8b7056f1/href">https://medium.com/media/1b745d87344b6acea5ba911a8b7056f1/href</a></iframe><p><em>Épisode disponible également sur </em><a href="https://deezer.page.link/aefFzKpzt5VyqJHH6"><em>Deezer</em></a><em> | </em><a href="https://podcasts.apple.com/fr/podcast/deez-is-la-tech/id1648116961?i=1000656324851"><em>Apple Podcasts</em></a><em> | </em><a href="https://open.spotify.com/episode/5zySrwNyo6qZ216nRzyJDT?si=kfkLSaWKQFyMpsDlJZSPvw"><em>Spotify</em></a><em> | </em><a href="https://music.amazon.fr/podcasts/c0253c4d-c100-46d9-a660-d1b5f93cc69f/episodes/5852803b-fd03-42a4-954b-a5f4124de015/deez-is-la-tech-s02e06---good-cop-bad-cop-comment-organiser-et-animer-une-communaut%C3%A9-de-pratique"><em>Amazon Music</em></a><em>.</em></p><h3>Transcription</h3><p><strong>[00:00:02.050] — Vincent : </strong>Bonjour et bienvenue dans Deez is la tech, le podcast qui ne pète ni les plombs, ni les crons ! Créé et animé par les équipes Product &amp; Tech de Deezer, ce programme aborde des sujets relatifs aux mondes de la tech et du streaming musical, et vous fait occasionnellement découvrir les coulisses de certaines des fonctionnalités phares de Deezer. Rejoignez-nous chaque mois pour une nouvelle discussion entre collègues et pairs, en toute décontraction, mêlant partages d’expériences, bonnes pratiques et réflexions sur les tendances futures. Prêts pour un nouvel épisode ? Chaussez vos écouteurs, ça commence maintenant !</p><p><strong>[00:00:42.240] — Vincent :</strong> Si vous évoluez dans le milieu de la tech, l’acronyme COP vous est certainement familier. Sinon, le terme vous évoque probablement les fameuses COP21, COP27 ou dernièrement, COP28. Mais rien à voir avec le climat ici, puisque nous allons parler de “Communities of Practice” ou “Communautés de pratique” en français. Si les communautés de pratiques existent, de fait, depuis la nuit des temps, dès lors qu’il y a un échange entre personnes autour d’un intérêt commun et d’une volonté d’apprendre les uns des autres, le concept de COP n’a été théorisé que dans les années 90, dans la littérature portant sur la gestion des connaissances. Une COP est ainsi décrite comme, je cite, “un groupe de personnes ou d’institutions qui partagent un intérêt commun envers la même pratique ainsi qu’une volonté partagée d’améliorer leur expertise et leur expérience de celle-ci”. Depuis les années 2000, le nombre et l’importance des communautés de pratiques n’a fait que croître dans les organisations et elles apparaissent aujourd’hui comme des entités incontournables en termes de gestion de savoir, de partage de connaissances et d’apprentissage en entreprise. Comment fonctionne une COP ? Comment la fait-on vivre au quotidien et sur le long terme ? Comment les initiatives issues des COP cohabitent-elles avec les objectifs d’équipe ? Ce sont quelques unes des questions auxquelles répondront nos invités du jour.</p><p><strong>[00:01:57.880] — Denis : </strong>Bonjour, Denis Germain, je suis Site Reliability Engineer chez Deezer et je suis aussi COP leader.</p><p><strong>[00:02:05.640] — Vincent :</strong> Et pour la deuxième fois dans la même saison de Deez is la Tech !</p><p><strong>[00:02:09.090] — Denis : </strong>Oui, j’ai de la chance !</p><p><strong>[00:02:11.510] — Vincent :</strong> Souhail ?</p><p><strong>[00:02:12.230] — Souhail : </strong>Bonjour, je suis Souhail. Je suis Engineering Manager côté Listeners Front-end à Core Product <em>(note: “Listeners” est une sous-équipe de la startup, i.e. de l’équipe, “Core Product” chez Deezer qui, comme son nom l’indique, est en charge de développer les fonctionnalités principales du produit Deezer)</em>. Ça fait un an et demi que je suis chez Deezer et quand je suis arrivé, on m’a dit : “tu es lead de COP”.</p><p><strong>[00:02:26.620] — Denis : </strong>Ça me dit quelque chose…</p><p><strong>[00:02:29.700] — Loic : </strong>Il y a une expérience partagée !</p><p><strong>[00:02:31.860] — Vincent : </strong>Et Nicolas ?</p><p><strong>[00:02:32.970] — Nicolas : </strong>Bonjour, moi, c’est Nicolas. Je suis Expert QA chez Deezer depuis quatre ans et j’anime la COP QA depuis au moins deux ans.</p><p><strong>[00:02:45.540] — Loic : </strong>Du coup, première question pour vous trois : c’est quoi une COP ?</p><p><strong>[00:02:49.620] — Denis : </strong>Moi, j’aime bien faire un peu d’historique — je suis pénible avec ça. C’est un terme qui a été inventé par Étienne Wenger — qui est un théoricien de l’éducation, je crois — dans les années 90. C’est une communauté, un groupe de personnes qui partagent un domaine, un centre d’intérêt commun, qui vont se réunir régulièrement pour discuter ensemble de ce sujet et surtout partager leurs réussites et leurs échecs. Ça, c’est pour la définition scolaire. Et comme je suis quelqu’un de très scolaire, je me sentais obligé de la faire.</p><p><strong>[00:03:18.430] — Souhail :</strong> Je pense que je ne peux pas rivaliser avec la définition scolaire ! Après, pour moi, c’est rassembler des personnes de talent pour avoir de l’intelligence collective, pour penser à faire des bonnes pratiques, évangéliser certains savoir-faire et profiter de l’intelligence de l’un et de l’autre.</p><p><strong>[00:03:41.240] — Nicolas :</strong> Je rajouterais que c’est aussi un lieu d’apprentissage où l’on apprend des autres. Je pense que c’est un terme important dans la définition de la COP.</p><p><strong>[00:03:51.080] — Loic : </strong>Apprentissage, transmission de connaissances…</p><p><strong>[00:03:53.173] — Nicolas : </strong>Oui.</p><p><strong>[00:03:53.290] — Vincent :</strong> Comment ça se passe ? Je serais curieux de voir vos descriptions à chacun pour voir si toutes les COP fonctionnent un peu de la même manière. Comment vous les appréhendez ? Qui vient ? Comment ça s’organise ?</p><p><strong>[00:04:07.330] — Loic : </strong>Comment ça fonctionne ?</p><p><strong>[00:04:08.560] — Nicolas :</strong> Je pense qu’il y a deux types de COP. Il y a la COP qu’on pourrait appeler de stack, qui va réunir un corps de métier. Par exemple, la COP QA, la COP Front-end, la COP Back-end, etc. Mais il y a aussi des COP de sujet — par exemple sécurité, Jira, etc. — qui sont plus des COP transverses avec différents corps de métiers. Donc déjà, il y a ces deux types de COP. Nous, pour la COP QA, ça va être tous les QA de chez Deezer. On se réunit deux fois par semaine : un premier point de synchronisation très opérationnel et un deuxième point beaucoup plus long pour aborder des sujets, faire des présentations autour du test, autour de la qualité, autour de nos pratiques mais aussi réfléchir parfois à des process, discuter, etc. Voilà comment est organisée notre COP côté QA.</p><p><strong>[00:05:10.540] — Loic : </strong>Côté Front-end ?</p><p><strong>[00:05:12.050] — Souhail : </strong>Côté Front-end, quand je suis arrivé, j’ai justement essayé de m’inspirer un petit peu de ce qui se fait chez Deezer, parce que dans mes autres expériences, la COP était organisée un peu différemment de ce que j’ai pu constater ici. Aujourd’hui, concrètement, on réunit tous les devs Front-end, autour de la stack, et on essaye justement de partager les connaissances, résoudre certaines problématiques, gérer le design system qui est sur Tempo<em> (note: Tempo est le nom du design system de Deezer)</em>. Pendant le revamp <em>(note: le rebranding de Deezer en 2023)</em> aussi, la COP était très utile pour partager la connaissance et l’avancement de ce projet qui a touché à toutes les stacks de toutes les équipes. Pour nous, aujourd’hui, ça peut être un format de workshop pour monter en compétences sur REC <em>(note : Remote Event Collector, notre outil de remontée d’évènements depuis les clients)</em>, ça peut être une présentation, par exemple “j’étais à la dernière conférence React 18, c’est ça qui est sorti récemment” — donc il y a quelqu’un qui y a assisté et qui partage avec les autres ce qu’il a pu constater pendant ces conférences — ou j’ai une problématique de performance sur mon site, comment je peux la résoudre ? C’est là où la COP peut justement donner des insights ou donner des idées aux personnes pour résoudre certaines problématiques.</p><p><strong>[00:06:21.780] — Loic : </strong>D’accord. Et Denis ?</p><p><strong>[00:06:23.310] — Denis :</strong> De mon côté, je suis lead de la COP SRE, Site Reliability Engineer. On a un utilisé ce terme fourre-tout pour le nom de cette COP parce qu’il y avait un gros déficit de bonnes pratiques d’infrastructure, de sécurité, de conteneurisation. Pourquoi on a fait ça ? Parce qu’il y avait un déficit, effectivement, de ce côté-là, on avait besoin de toucher un grand nombre de personnes. C’est une COP très transverse. Ce n’est effectivement pas comme une COP Front, une COP Back, où ce sont des gens du même métier qui viennent. Là, il y a plein de gens qui viennent de plein de métiers différents. Et le but, c’est vraiment de diffuser la connaissance. Donc on a eu une grosse phase — ça fait trois ans que la COP existe — où c’était surtout des présentations : présentation sur tel sujet, la sécurité dans les conteneurs par exemple, présentation sur tel autre sujet… Ce n’est pas tout à fait la façon dont je conçois les COP parce que c’est quand même quelque chose qui est censé être très horizontal, où chacun est capable de parler, chacun est capable de présenter ses sujets. Mais au début, c’était important parce qu’il y avait ce déficit de diffusion de bonnes pratiques. Nous, on se réunit tous les 15 jours, normalement, deux fois par mois. Et le but du jeu, c’est d’aller chercher des gens pour essayer de trouver des sujets, puisque sinon ce sont toujours les mêmes personnes qui présentent les mêmes choses. Et même s’il y a toujours plein de sujets et plein de choses à présenter, c’est quand même mieux si tout le monde est capable de partager.</p><p><strong>[00:07:54.210] — Loic : </strong>Comment vous faites pour faire en sorte qu’il y ait des sujets à partager ? Comment vous appâtez le chaland pour venir présenter un truc ?</p><p><strong>[00:08:01.190] — Denis : </strong>Le truc qui est très intéressant dans les COP, comme on est sur quelque chose de très horizontal où l’on veut que justement les gens partagent, moi, ce que j’aime bien, c’est instaurer un climat relativement bienveillant et aussi très informel. Je n’hésite pas à faire des blagues, je n’hésite pas à mettre des memes dans mes présentations pour montrer aux gens que l’on est gentil, on est là pour partager entre nous. Je ne sais pas si toutes les COP respectent ce format, mais le but du jeu dans la COP que j’anime, c’est vraiment de montrer que tout le monde est capable de présenter quelque chose puisque tout le monde fait des choses intéressantes. Chez Deezer, on a des techs merveilleux. Tout le monde fait des trucs extraordinaires et ne s’en rend pas forcément compte. Et c’est important de venir partager ça aux autres parce que parfois, on se rend compte que pour un même sujet, il y a déjà trois-quatre équipes différentes qui ont résolu le problème de trois-quatre façons différentes. Ça ne veut pas dire que l’on aurait pu résoudre ça d’une seule manière, mais c’est intéressant de confronter ces différentes façons de faire. Aller chercher des gens, c’est compliqué donc la première étape, c’est de montrer que si jamais ils présentent un truc, ils ne se feront pas taper dessus.</p><p><strong>[00:09:09.650] — Nicolas : </strong>Nous, on a une boîte à idées. On a un Trello où l’on met des cartes avec les sujets que l’on veut aborder ou quand on pense que telle équipe a fait un truc intéressant. L’avantage de mon rôle d’expert, c’est que j’ai une vision un peu plus globale de ce qui se fait chez Deezer, dans les autres équipes que la mienne. Ça me permet d’aller chercher directement les gens en leur disant : “Est-ce que tu ne voudrais pas présenter ça ? Est-ce que tu ne voudrais pas partager ça ?” Nous, on a quand même du mal à aller chercher les gens. On a quand même du mal à avoir des gens qui disent : “Oui, j’ai envie de présenter ça”. Il faut forcément aller les chercher. Ce n’est pas évident parce qu’on a beaucoup de gens qui ne se sentent pas capables d’animer une COP, que ce soit même pendant une demi-heure ou une heure. Aussi, on a une deuxième difficulté dans notre COP, c’est qu’elle est en anglais et on n’a pas tous un niveau d’anglais bilingue. Je prends mon cas : mes présentations en anglais sont beaucoup moins dynamiques et beaucoup moins spontanées que si je la faisais en français. Du coup, certaines COP que je fais peuvent perdre en impact et perdre en dynamisme. Donc forcément, on perd les gens pendant l’heure de présentation.</p><p><strong>[00:10:32.020] — Loic : </strong>D’accord. La raison pour laquelle elle est en anglais, c’est parce qu’il y a beaucoup d’anglophones ?</p><p><strong>[00:10:35.300] — Nicolas :</strong> Oui, on a une partie d’anglophones dans les équipes QA.</p><p><strong>[00:10:39.530] — Souhail : </strong>Côté Front, ça a beaucoup évolué depuis que j’ai commencé à animer la COP. Au début, c’était à moi d’aller chercher des sujets, d’aller chercher des volontaires pour faire des présentations. Mais petit à petit, j’ai essayé de chercher des alliés — on va dire — pour m’aider à animer la COP. Je me suis basé, ou je me suis fait aider par les Leads Front, qui sont les experts dans les trois startups <em>(note: il y a trois startups, i.e. trois grosses équipes au sein du département Product &amp; Tech de Deezer)</em>. À partir de là, on a essayé en Front Lead meeting toutes les semaines de se demander : “C’est quoi les sujets pertinents ? C’est quoi les urgences qui vont arriver ? Est-ce qu’il y a un sujet un peu transverse qui peut impacter tout le monde, que l’on peut traiter et présenter à la COP ?” Petit à petit, on a commencé à réfléchir et j’avoue que j’ai passé le flambeau pour qu’ils soient plus autonomes sur cette partie-là. Je suis arrivé dans un contexte où il fallait reconstruire l’équipe Listeners Front-end, il fallait faire des recrutements et faire grossir l’équipe. Avec l’urgence et le workload associé, je n’avais plus le temps pour animer la COP et aller chercher des volontaires pour présenter. Aujourd’hui, les Leads Front sont beaucoup plus autonomes sur l’animation de la COP. On a un Trello justement, comme une boîte à idées, pour se dire : “Moi, je veux savoir comment marche le player”, “je veux savoir comment marchent les pages dynamiques”… Après, il y a aussi des volontaires disent : “Moi, j’ai cette connaissance-là, je peux faire une présentation à la COP” et on essaye de planifier ensemble. Ensuite, ils ont aussi proposé un format avec un “Zizou”, un milieu de terrain qui permet d’animer la COP. C’est un rôle qui est tournant, donc à chaque fin de COP, on choisit un “Zizou” pour animer la prochaine COP. Ce sera son rôle de prioriser les sujets. On a aussi fait un template de présentation que l’on suit à chaque COP, où l’on va dérouler les différents sujets. C’est le “Zizou” qui va passer la parole d’un présentateur à l’autre. Donc aujourd’hui, c’est beaucoup plus structuré, la charge de travail est partagée par tout le monde. Ça permet de responsabiliser tout le monde, donner du sens à la COP et donner du sens aux présentations que l’on est en train de faire.</p><p><strong>[00:12:56.170] — Vincent : </strong>Et depuis que vous avez mis en place cette formule — je crois que c’est relativement récent à l’heure où l’on enregistre, je crois que ça va faire deux mois — est-ce que vous avez senti un regain d’intérêt et un regain d’implication des différentes personnes du front-end, en l’occurrence, pour la COP ?</p><p><strong>[00:13:14.780] — Souhail : </strong>Forcément, parce que ça casse la barrière de la peur. Tout le monde peut animer une COP, tout le monde est capable de présenter un sujet à la COP. Il n’y a pas de tabou, il n’y a pas de : “J’ai peur, je n’ai pas le niveau” ou “je n’ai pas les épaules pour présenter une COP”. On a essayé de casser cette barrière-là au début pour dire que le but n’est pas de juger les personnes, c’est un milieu d’entraide. On a essayé de beaucoup jouer sur la sécurité psychologique au début. Aujourd’hui, quand on responsabilise les gens pour aller chercher des sujets, pour dire “ça, c’est intéressant à présenter”, et qu’en tant que manager, on les aide à faire leurs présentations, etc., je pense que ça casse la barrière de la peur, ça responsabilise plus les gens et ça donne plus envie aux gens de s’impliquer et de participer à la COP. Sachant qu’au début, la fréquence était hebdomadaire, mais on a vu que c’était un peu short pour préparer des présentations donc on l’a passée à tous les 15 jours. Il ne faut pas avoir peur aussi d’annuler une COP si jamais il n’y a pas de sujets à présenter. Il ne faut pas forcer les gens à aller présenter des sujets s’ils ne sont pas prêts.</p><p><strong>[00:14:28.090] — Nicolas :</strong> Nous, on applique la même technique. Si on n’a pas de sujet, on annule. Généralement, on a un créneau de présentations toutes les semaines. D’ailleurs, ce ne sont pas que des présentations, on essaye de varier les formats. Ça peut être, effectivement, la présentation un peu scolaire. Ça peut être ce que je vais appeler un “talk” : on va discuter autour d’un ou plusieurs sujets avec quelqu’un qui va prendre des notes. On a aussi des workshops : on essaye de faire des workshops, soit de tests quand on a des nouvelles features qui vont arriver et que l’on a besoin d’une multitude de retours, soit aussi pour réfléchir sur des process, sur des méthodes, etc. Enfin, on a aussi des temps de “team building”, que l’on essaye de faire au moins une fois par semestre, où l’heure de COP va être consacrée à faire un jeu juste pour -comment dire — “émulsionner”…</p><p><strong>[00:15:30.110] — Loic : </strong>C’est de la cuisine !</p><p><strong>[00:15:30.140] — Souhail : </strong>Créer de la cohésion.</p><p><strong>[00:15:31.680] — Nicolas : </strong>Oui, voilà ! Pour avoir un esprit d’équipe.</p><p><strong>[00:15:35.740] — Loic :</strong> Cet “esprit d’équipe” revient souvent. J’ai l’impression que c’est la même chose que l’on ferait dans une équipe quelconque, peu importe le staffing de l’équipe. C’est de faire en sorte de créer un contexte où c’est à la fois cool, à la fois utile et à la fois, on a envie d’y aller.</p><p><strong>[00:15:52.150] — Nicolas :</strong> Oui, il faut avoir des gens qui sont intéressés, qui sont investis et qui n’aient pas l’impression de perdre leur temps. Nos COP sont facultatives. Les COP de type présentation sont facultatives parce que si l’on a des gens qui viennent contre leur gré, il n’y a pas de participation, il n’y a pas cette émulsion, il n’y a pas d’échange. Et ça donne des situations un peu “pénibles” — on va dire — pour le présentateur ou pour l’animateur, pour celui qui propose le sujet, parce qu’en fait, il a l’impression d’avoir échoué parce qu’il a partagé son sujet, mais ça n’avait l’air d’intéresser personne. Alors que si on n’a que des gens qui viennent parce que ça les intéresse, il y a déjà un peu plus de discussions, il y a plus de questions. Donc la personne qui anime va se dire : “J’ai réussi ma présentation ou ma COP parce que ça a intéressé les gens, ils ont participé, ils ont montré qu’ils avaient réussi à accrocher à mon sujet”.</p><p><strong>[00:16:58.160] — Souhail : </strong>Justement, je trouve très intéressant le concept de cohésion dans la COP, parce que quand on est manager d’une équipe, c’est normal de créer cet esprit d’équipe, de la cohésion. On est au quotidien avec l’équipe donc on les connaît très bien. Mais au sein d’une COP, on rassemble les devs de toutes les startups. Moi, j’étais dans un contexte où il y avait eu une réorg avant que j’arrive chez Deezer. Il y avait l’équipe Core, qui était une équipe un peu transverse qui développe des outils pour les autres équipes comme REC, le player, le SDK du player, etc. Il y avait aussi Tempo, le design system, qui était sur une technologie qui s’appelle Chakra, et tout le monde n’était pas d’accord sur ce concept-là. Donc j’arrive à un moment où l’on me dit qu’il y a des tensions entre les équipes. Donc, au début, il fallait justement créer cette cohésion d’équipe, rassembler tout le monde et dire : “On est tous Deezer, on est tous la même équipe”. Mon ambition à l’époque, c’était justement d’atteindre le niveau 5 du management tribal. C’est un livre que j’ai lu il y il y a quelques années qui m’a beaucoup influencé, “Manager votre tribu” de Dave Logan. C’est comme une boîte à outils pour transformer une équipe d’une phase à une autre. En fait, il divise la société en cinq niveaux, cinq tribus. La première et la deuxième, on n’en parlera pas parce que ce sont des sociétés de gangs, d’ex-taulards et trucs comme ça. Mais les trois niveaux qui m’intéressent, ce sont : le niveau trois où c’est “je suis le meilleur, mais pas les autres”, le niveau quatre où c’est “nous sommes les meilleurs, mais pas les autres, et le niveau cinq où c’est “nous sommes les meilleurs, mais comment fait-on pour que les autres soient meilleurs ?” Et c’est ce niveau-là que je voulais atteindre avec la COP. OK, nous, on a du savoir-faire côté Listeners sur la partie player, chez Partnerships <em>(note : l’une des trois startups de la division Product &amp; Tech de Deezer)</em>, ils ont de la connaissance sur d’autres parties, chez CM <em>(note : Customer &amp; Monetization, la troisième startup de la division Product &amp; Tech de Deezer)</em>, ils ont beaucoup de connaissance sur la partie unlogged <em>(note : pages non-loguées, pour les utilisateurs qui ne sont pas connectés à un compte Deezer)</em>, sur la partie freemium, etc. Comment on rassemble toute cette intelligence collective et comment on travaille ensemble pour atteindre un objectif commun ?Aujourd’hui, un projet concret, c’est que l’on souhaite changer la techno du design system, qui est Chakra, par un autre système. Et justement, on est en train de travailler ça collectivement. On est en train d’essayer de faire des POC <em>(note : Proof-Of-Concept, une réalisation expérimentale concrète et préliminaire illustrant une certaine méthode ou idée afin d’en démontrer ou pas la faisabilité)</em> ensemble, de partager nos expériences et d’éviter que l’on reproduise le même schéma que pour Chakra avec des tensions et en obligeant les autres à adopter des décisions qui ne sont pas collectives.</p><p><strong>[00:19:33.070] — Loic : </strong>D’ailleurs, ça a donné lieu à la création d’une autre COP sur le design system.</p><p><strong>[00:19:38.310] — Souhail :</strong> Après, dans l’autre COP, il y a l’équipe Design qui est impliqué. Et ça a plus de sens, justement.</p><p><strong>[00:19:44.510] — Loic : </strong>Oui, il y a deux niveaux de discussion. Il y a la partie technique dont tu parlais : comment tu implémentes, comment les choses vont être mises en place dans les différentes stacks… Vous en parlez côté Front mais je pense que c’est aussi côté iOS et Android qu’il doit y avoir des discussions — il y a aussi des COP sur ces stacks-là. Mais il y a aussi une COP complètement transverse qui s’est montée avec des designers, avec des développeurs des différentes stacks qui discutent du design system, suite aux discussions qui ont pu avoir lieu dans les différentes COP à ce moment-là.</p><p><strong>[00:20:17.180] — Souhail : </strong>Exactement.</p><p><strong>[00:20:27.330] — Vincent : </strong>Ces COP sont transverses aux équipes, donc elles sont vraiment trans-startups — puisque c’est comme ça que l’on appelle nos grosses équipes chez Deezer. Est-ce que vous arrivez à garder une espèce de cohérence dans vos COP respectives, malgré les potentiels tiraillements d’objectifs que l’on peut avoir selon l’équipe dans laquelle on travaille au day-to-day ?</p><p><strong>[00:20:46.990] — Denis : </strong>C’est justement tout l’intérêt des COP. Comme tu l’as dit, ce sont des organisations qui sont transverses. C’est une organisation dans l’organisation. Ce sont des équipes complètement en dehors de toute hiérarchie existante. Le terme “communauté” est important : c’est une communauté, ce sont des gens qui se retrouvent, il faut qu’ils fassent corps ensemble. Et justement, par rapport aux objectifs, ce que j’aime bien faire dans notre COP, c’est fixer des objectifs sur des sujets qui intéressent la COP. Nécessairement, ça va être contradictoire par rapport aux objectifs d’une équipe, puisque l’objectif de l’équipe va être d’avancer le plus vite possible sur ces sujets à elle. Pour autant, on a des sujets transverses, on doit faire avancer toute l’entreprise. Il ne faut pas hésiter à prendre des sujets qui n’ont rien à voir avec les sujets des équipes. Le but, ce n’est pas de favoriser une équipe par rapport à une autre. Le but, c’est de “tacler” — j’utilise cet horrible mot, cet horrible anglicisme — des sujets qui sont transverses. Et c’est justement parce que l’on est hors de toute organisation que l’on peut se permettre de faire ce genre de changements qui seraient difficiles à faire accepter si c’était une équipe qui demandait à une autre de le faire.</p><p><strong>[00:22:03.160] — Loic : </strong>On est d’accord que, du coup, c’est nécessaire d’avoir du volontariat, des personnes impliquées, voire même des équipes…</p><p><strong>[00:22:12.140] — Denis :</strong> Plus que du volontariat, il faut même avoir un soutien hiérarchique — j’ai envie de dire — puisqu’effectivement, les objectifs de la COP, de la communauté en tant que telle, peuvent être contradictoires avec les objectifs à court terme d’une équipe ou de toutes les équipes qui la composent. Donc nécessairement, il faut qu’il y ait un un soutien d’abord des membres de la communauté, on en a parlé : il faut que ce soit des gens qui se sentent heureux d’être ensemble, qui sentent ce côté communauté, ce “faire corps avec la communauté”. Et il faut aussi un soutien de la hiérarchie. Parce que si tu n’as pas de soutien, derrière, tu vas dire “on a un objectif de COP” et puis le lead de chaque équipe ou le manager de chaque équipe va dire “oui, très bien, mais on doit livrer telle feature en Q1”, “à la fin du sprint, il faut que l’on ait rempli les trois tickets”… Donc nécessairement, oui, ça nécessite de la volonté, ça nécessite de faire corps avec sa communauté. C’est à la fois tout l’intérêt et toute la difficulté.</p><p><strong>[00:23:09.260] — Nicolas :</strong> Il faut un sponsoring de sa COP. Il faut effectivement que le management supporte les COP. La COP, aussi, ça ne va pas être imposer quelque chose, ça va être proposer quelque chose. On va proposer un cadre, on va proposer des évolutions, etc. Il faut vraiment l’adhésion de tout ça pour que les objectifs de COP fonctionnent.</p><p><strong>[00:23:33.170] — Souhail : </strong>C’est pour ça que les objectifs de la COP ne peuvent pas être sur le court-moyen terme, ce sont plutôt des projets sur le long terme et il faut s’accrocher. Il faut s’accrocher et il faut aussi suivre les actions, avoir des KPIs, des métriques peut-être, pour aussi montrer l’avancement et ne pas perdre — on va dire — le “soleil radieux”. Il y en a qui vont rigoler en entendant ça.</p><p><strong>[00:23:57.980] — Nicolas : </strong>Il faut rendre ces actions visibles, en fait.</p><p><strong>[00:24:01.550] — Loic :</strong> Il faut un outil de suivi de toute façon.</p><p><strong>[00:24:03.280] — Nicolas : </strong>Oui, il faut un outil de suivi et il faut surtout que ces actions soient accessibles et visibles, notamment auprès du management. Nous, c’est une difficulté que l’on a : on a des actions mais elles ne sont pas tracées, elles ne sont pas visibles dans les boards Jira. Donc forcément, quand on dit “il faut que l’on travaille sur ce sujet de COP ou sur cette action de COP”, comme ce n’est pas visible, ce n’est pas compté comme une tâche de sprint, par exemple. Du coup, ces actions-là restent sous-marines et on n’arrive pas à promouvoir notre COP et nos actions.</p><p><strong>[00:24:43.790] — Vincent : </strong>Pour l’avoir vécu — je vais prendre ma casquette d’ancien lead de COP, ce que l’on avait essayé de faire aussi, c’était justement de tracer ces actions. Par contre, ce que l’on avait choisi comme format, c’était de faire des tickets Jira et d’essayer de les faire avancer. Et la grosse difficulté que l’on a, justement, par rapport à ça — je me demande comment vous le gérez au quotidien, c’est que ces actions, comme vous le dites, ce sont des actions sur le long terme. Le problème du ticket Jira sur un board, c’est que quand il n’avance pas, ça peut être assez démotivant. Comment vous gérez vos actions pour faire en sorte que ce ne soit pas trop démotivant pour les équipes de voir que, finalement, c’est du travail de fond ?</p><p><strong>[00:25:16.500] — Loic : </strong>Oui, parce que les temporalités ne sont peut-être pas les mêmes par rapport aux projets des équipes en tant que tels.</p><p><strong>[00:25:22.000] — Denis : </strong>Si je prends l’une des initiatives que l’on avait prises il y a un moment déjà, qui était de sécuriser l’une des parties de nos conteneurs, il y a des équipes qui avancent plus vite que d’autres. Du coup, comme c’est très transverse, comme il y a beaucoup d’équipes différentes, d’une fois sur l’autre, il y a toujours une équipe qui a avancé un petit peu. Finalement, il y a toujours du progrès. Les équipes qui se lancent tout de suite dans l’action, elles vont faire avancer le truc, ça va donner envie aux autres équipes de faire pareil, parce que l’émulation du groupe fait que l’on a envie de faire aussi bien que les autres. Et c’est un cercle vertueux, en fait. Donc il faut trouver les sponsors, ceux qui vont avoir la bande passante pour pouvoir initier le sujet, commencer à faire avancer le sujet. Et après, on capitalise là-dessus en disant à la COP suivante : “voilà, telle équipe a réussi à faire ça, ils ont eu telles difficultés, ils ont fait comme ci, ils ont fait comme ça”. Et la fois d’après, naturellement, il y a une autre équipe qui s’y est mise. Ça ne va pas marcher à tous les coups, mais c’est l’une des façons d’y arriver dans mon cas. Les gens, ils ont envie de bien faire et il faut juste leur montrer la direction.</p><p><strong>[00:26:32.160] — Souhail : </strong>Pour moi, aujourd’hui, c’est un peu difficile de prioriser des sujets “techniques” — on va dire — de la COP dans les sprints ou dans les streams <em>(note : les streams chez Deezer sont des entités de travail formées pour apporter des solutions à un problème ou une opportunité définis)</em>. Le challenge, c’est d’atteindre ce niveau de rigueur, de rédiger un PIB, de passer en PAM pour justement…</p><p><strong>[00:26:53.200] — Vincent :</strong> Il va falloir que tu expliques ce que sont un PIB et un PAM parce qu’en dehors de Deezer, je crois que personne ne sait de quoi on parle !</p><p><strong>[00:26:58.150] — Souhail : </strong>On a un process une fois qu’une opportunité est identifiée. Il faut rédiger un PIB <em>(note : Product Initiative Brief)</em> qui va expliquer quelle est l’opportunité, quel KPI on veut toucher, quelles sont les métriques de succès. Ensuite, passer en PAM, c’est le Product Alignment Meeting où l’on va pitcher devant le CTO, les VP, les Heads-of, cette opportunité, et avoir un go / no-go pour commencer cette initiative. Pour moi, si l’on arrive à identifier un sujet de COP qui est intéressant, qui a un impact, que ce soit sur notre manière de travailler, améliorer le time-to-market, améliorer notre CI ou améliorer la sécurité, etc., il faut que l’on soit capable d’avoir la rigueur de passer ce processus-là et de planifier ce genre d’initiatives techniques avec les initiatives produit.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ejQgcyoRL_9cgA9VnESpEw.jpeg" /></figure><p><strong>[00:27:51.830] — Loic : </strong>Dans les COP, il y a des discussions, on parle de faire avancer des sujets de COP. Est-ce qu’il y a des décisions qui sont prises dans les COP ? Et au-delà de “est-ce qu’il y en a ou pas ?” — je pense qu’il y en a, comment ces décisions sont-elles prises et comment sont-elles respectées ? Il y a potentiellement des gens qui ne sont pas présents pendant les COP — je pense notamment à des COP qui vont être autour de stacks, où l’on dit que c’est facultatif. Si des décisions sont prises, qu’est-ce qui se passe quand — je ne sais pas — 75% des gens de la stack sont présents et que les autres ne sont pas venus pour raison X,Y ? Sachant qu’il suffit d’être en vacances pour ne pas venir.</p><p><strong>[00:28:28.980] — Denis : </strong>Comme il n’y a pas de hiérarchie dans les COP, les décisions sont prises de manière collégiale. Comme tu dis, effectivement, ça pose un problème pour les gens qui ne seront pas là. Eh bien, ils n’avaient qu’à être là !</p><p><strong>[00:28:39.630] — Nicolas :</strong> En vrai, je suis entièrement d’accord avec ça !</p><p><strong>[00:28:44.230] — Denis :</strong> Ouais, alors je blaguais un petit peu… Mais il y a un fond de vérité quand même !</p><p><strong>[00:28:47.330] — Nicolas :</strong> Il y a un fond de vérité quand même. Oui, la décision, de toute façon, elle est collégiale avec les personnes qui sont présentes, et ça veut dire que ce sont des personnes qui ont l’envie de faire avancer les choses. Donc il faut vraiment travailler avec elles. Comme je le disais tout à l’heure, pour moi, la COP n’impose pas, elle pose un cadre, elle propose un cadre. Après, il y a quand même des décisions qui sont plus structurelles, par exemple le choix d’un outil. On a fait le choix d’utiliser Xray, par exemple, pour notre patrimoine de test. Ça a été une décision qui a été prise après une task force faite avec quelques membres de la COP QA qui étaient intéressés. Pour ce genre de décisions structurelles, on essaye au moins d’impliquer une personne de chaque équipe pour être sûr que tous les besoins sont couverts. Pour d’autres décisions… Par exemple, on a travaillé sur un template pour remplir les tickets de bugs, et pour celui-là, il n’y avait pas tout le monde. On a essayé de couvrir la plupart des cas mais au final, il n’est implémenté que dans deux équipes.</p><p><strong>[00:29:58.610] — Vincent :</strong> Ça pose la question, j’imagine, du relais ensuite dans les équipes, puisque tout le monde ne vient pas forcément. Comment vous l’assurez ? Est-ce que ça se fait naturellement ? Que les personnes présentes pendant une décision arrivent à relayer ces informations-là et fassent en sorte qu’il y ait une vraie adoption de cette décision ?</p><p><strong>[00:30:18.540] — Nicolas :</strong> Nous, on utilise le point très opérationnel de synchronisation, qui est obligatoire, pour relayer des décisions prises. Tout le monde est au courant de ces décisions-là. Ensuite, toutes nos COP sont enregistrées, elles sont accessibles, on a une page qui liste tous les sujets avec la vidéo, les slides, etc. Donc si une personne qui n’était pas là veut plus d’informations ou se renseigner, elle a tous les éléments possibles pour le faire.</p><p><strong>[00:30:49.730] — Denis :</strong> Les sujets qui nécessitent des décisions dans des COP, souvent — comme tu l’as dit, ce ne sont pas des sujets qui sortent de nulle part. Ce n’est pas un jour qu’on se dit : “on va prendre une décision, on la prend, et puis, les gens qui n’étaient pas là, ce n’est pas grave, dommage pour eux”. Souvent, comme ce sont des décisions, ce sont des trucs assez structurants, ce sont des choses que l’on a pris le temps d’étudier déjà, ça ne sort pas de nulle part. Si une personne n’est pas du tout intervenue dans le process de décision, c’est que ça fait longtemps qu’elle ne vient pas à la COP. Reste le problème des équipes qui ne viennent pas du tout aux COP. Ça, c’est un autre souci. Il faut comprendre pourquoi les équipes ne viennent pas aux COP, pourquoi elles ne sont pas intéressées par cette COP en particulier — si on estime qu’elles sont censées venir. Mais les sujets qui ont été pris dans la COP SRE, notre COP — parce que je n’ai pas envie de dire que c’est “ma” COP, ce sont des sujets dont on parle quand même plusieurs fois avant de prendre une décision, surtout si c’est structurant. Ce n’est pas du jour au lendemain que l’on se dit : “voilà, maintenant, on va tous faire comme ci”. On en a déjà parlé avant, on a déjà présenté la problématique, on a expliqué, on s’est mis d’accord et à la fin, on prend la décision. Et les gens qui ne sont pas là au moment de la décision, dommage. Mais après, on peut toujours revenir en arrière. Aucune décision n’est jamais définitive. Si jamais il y a un problème, on peut revenir dessus.</p><p><strong>[00:32:14.720] — Loic :</strong> Je peux rajouter des choses — pareil que Vincent, je prends une autre casquette. Il y a une COP que j’anime, qui est la COP GraphQL, qui est très transverse. Quand on prend une décision dans cette COP-là, c’est une décision qui va concerner énormément de gens dans énormément d’équipes différentes et il n’y a réellement pas d’ownership potentiel pour prendre cette décision en dehors de cette COP-là. Ce que l’on a tendance à faire, c’est que les gens qui sont là vont en discuter — c’est à ce moment-là qu’il va y avoir les débats les plus fournis, généralement. Par contre, ça va se faire aussi de manière asynchrone. On va utiliser énormément Slack. En tout cas, c’est ce que j’ai poussé le plus : dès que quelqu’un a une question ou a un sujet à apporter, il y a toujours possibilité de discuter de vive voix pendant les meetings de COP, mais il y a toujours une étape de discussion ou au moins de compte rendu dans les channels, de manière à ce que tout le monde qui est au moins dans le channel Slack — ça fait plus de monde que de monde qui participe à la COP elle-même — soit au courant de ce qui s’est dit et du fait que ça va avoir des impacts. Pour moi, la partie asynchrone a tendance à être plutôt efficace, mais ça veut aussi dire que c’est quelque chose qui n’arrive pas systématiquement. C’est fait quand c’est nécessaire, quand on se dit : “il y a un impact sur beaucoup de gens, on était peut-être six à en discuter, mais on est 45, voire 100, à réellement “subir” — dans le sens positif ou négatif — les impacts de cette décision”.</p><p><strong>[00:33:51.350] — Vincent :</strong> Comment fait-on vivre une COP dans la durée ? Comment s’assure-t-on qu’il y ait toujours un intérêt des équipes ? Comment s’assure-t-on que ça réponde toujours aux besoins ? Comment faites-vous ça chacun de votre côté ?</p><p><strong>[00:34:04.400] — Denis :</strong> C’est un peu naïf, mais déjà, le premier indicateur que l’on peut regarder, c’est : est-ce que les gens reviennent ? Si les gens ne reviennent pas, c’est qu’il y a quelque chose qui ne va pas. On peut toujours se poser la question et demander aux gens qui ne viennent pas pourquoi ils ne viennent pas. C’est quelque chose que je fais : je garde la liste, pas des gens qui viennent, mais du nombre de personnes qui viennent pour tous les sujets et je passe, à la fin de chaque COP, un lien avec un petit programme développé maison qui demande aux gens de voter de 1 à 5 de manière anonyme — 1 étant la note la plus basse, “j’ai perdu mon temps”, et 5 étant “j’ai utilisé mon temps de la manière la plus productive possible sur cette heure de réunion”. J’essaye de suivre ça et j’essaye de regarder quels sujets ont le plus plu. C’est une manière déjà, a minima, de faire revenir les gens. Alors, est-ce que faire venir les gens, c’est faire vivre la COP ? On peut en débattre, mais déjà, elle continue à exister.</p><p><strong>[00:35:06.310] — Souhail : </strong>Concernant la COP Front, elle survit aujourd’hui grâce au travail des Leads Front. Ils font un travail énorme et magnifique pour être à jour sur les sujets, pour trouver des idées sympathiques pour animer la COP. Parfois, on fait des rétros <em>(note : rétrospectives)</em>, parfois, il y a des jeux, des blind tests, etc. Justement, on introduit aussi le côté fun dans la COP pour créer cette cohésion et faire survivre cette COP. Après, les clés de la réussite pour garder des gens motivés, c’est plus les impliquer, varier les sujets et varier le format aussi. Récemment, il y a eu pas mal d’autres personnes d’autres COP qui sont venues à notre COP Front pour nous présenter des trucs, que ce soit sur la QA, que ce soit sur la sécurité — comment on sécurise un conteneur Docker, etc. Je trouve ça aussi intéressant de sortir parfois du cadre du front-end, du React <em>(note : librairie pour les interfaces utilisateurs web et natives)</em>, du SSR <em>(note : “Server-Side Rendering” ou rendu côté serveur d’une page web)</em>, etc., pour donner plus de visibilité aux développeurs sur ce qui se passe dans l’écosystème de développement au global.</p><p><strong>[00:36:12.780] — Nicolas :</strong> Nous, on va utiliser le ROTI — enfin, moins ces derniers temps — mais on essaye de faire un ROTI en fin de COP pour voir un petit peu les sujets qui ont marché ou moins bien marché. Ça peut être aussi dû au format qui n’était peut-être pas le bon, par exemple, pour un sujet. Nous, on demande un commentaire. Notamment, on espère que les personnes qui mettent 1, 2 ou 3 expliquent pourquoi elles ont mis 1, 2 ou 3 : qu’est-ce qui ne leur a pas plu ?Ou pourquoi elles ont perdu leur temps ? On fait aussi, une fois par semestre, une rétro pour essayer d’améliorer les choses, que ce soit dans la sélection des sujets, comment on fait les meetings, etc.</p><p><strong>[00:36:57.350] — Denis : </strong>Le ROTI, c’est le “Return On Time Invested”. C’est la petite note de 1 à 5 dont je parlais tout à l’heure — pour nos auditeurs qui auraient l’eau à la bouche !</p><p><strong>[00:37:05.620] — Loic :</strong> On fait de la cuisine donc, dans ce podcast, pour la deuxième fois.</p><p><strong>[00:37:10.360] — Denis : </strong>Musique et cuisine.</p><p><strong>[00:37:11.960] — Vincent :</strong> Est-ce que pendant les rétros que vous avez pu faire — j’imagine que ce sont les personnes de la COP qui viennent proposer des choses — vous avez souvent des changements qui peuvent être un peu disruptifs ou est-ce que c’est souvent à la marge ? Comment ça évolue ? Est-ce qu’il y a des moments où ça tranche vraiment de formules ou est-ce que ce sont des petites adaptations au fil du temps ?</p><p><strong>[00:37:34.810] — Nicolas : </strong>Nous, on a fait beaucoup de petites adaptations au fil du temps. Après, on a tous — c’est ce qu’on disait tout à l’heure — une difficulté à suivre les actions. On a tous notre quotidien et des fois — souvent même, on a du mal à introduire les gros changements que l’on aimerait faire. Et si on veut faire un “breaking change”, on ne le fera jamais. Et donc, ce qui marche, c’est vraiment de faire des petites adaptations au fur et à mesure.</p><p><strong>[00:38:04.910] — Souhail :</strong> Je vais parler au nom des Lead Front, mais au travers des rétros, on a pris des renseignements sur ce qui ne marche pas, ce qu’on peut améliorer. Pareil que côté QA, c’était des petits… On a ajouté le “Zizou”, on a amélioré les slides. C’était des petits ajustements qui ont permis de faire vivre et de donner envie à tous les devs Front de venir toutes les deux semaines à la COP.</p><p><strong>[00:38:32.300] — Nicolas : </strong>La difficulté, c’est que quand on fait des rétros, il faut que les personnes qui ne viennent pas aux COP viennent aux rétros pour nous expliquer pourquoi.</p><p><strong>[00:38:42.680] — Loic :</strong> Problème récurrent.</p><p><strong>[00:38:42.950] — Nicolas : </strong>C’est le serpent qui se mord la queue.</p><p><strong>[00:38:45.260] — Denis :</strong> Après, il y a un sujet tabou, mais les COP n’ont pas forcément vocation à vivre éternellement. Peut-être qu’à un moment donné, la COP n’est plus pertinente et doit disparaître. Donc ce n’est pas forcément un but en soi de maintenir en vie une COP ad vitam aeternam. Potentiellement, un jour, elle ne sera plus nécessaire.</p><p><strong>[00:39:04.970] — Loic :</strong> Je comprends mais, par contre, il y a des COP…</p><p><strong>[00:39:06.130] — Denis :</strong> Il y a des exceptions !</p><p><strong>[00:39:06.500] — Loic : </strong>…où il va y avoir des enjeux à les maintenir en vie peut-être plus importants, je pense, que d’autres COP.</p><p><strong>[00:39:14.160] — Vincent : </strong>Oui, parce que j’imagine que si l’on dit “on arrête la COP sécurité parce qu’on n’en a plus besoin”, ça va potentiellement poser des soucis !</p><p><strong>[00:39:21.040] — Nicolas : </strong>Sur la COP QA, c’est par phases. On va avoir des phases où il va y avoir beaucoup d’activités, beaucoup d’émulsion parce qu’on a des nouvelles idées, on tente des choses, etc. Et on va arriver au bout de deux ou trois ans à une période un peu plus calme où l’on va annuler énormément de COP, où l’on va se reposer la question, effectivement, de la légitimité de cette COP. Après, c’est une COP de stack, donc on va au moins garder le point de synchronisation opérationnelle. On a eu des périodes où l’on n’avait que ça.</p><p><strong>[00:39:55.520] — Loic : </strong>Mais du coup, ça a fonctionné pendant cette période ?</p><p><strong>[00:39:57.070] — Nicolas :</strong> Oui, oui.</p><p><strong>[00:39:58.200] — Vincent :</strong> Ça peut être aussi une formule, finalement, de se dire : “là, on n’a rien de folichon à se raconter par ailleurs, par contre, on a besoin…”</p><p><strong>[00:40:05.340] — Nicolas : </strong>Ça permet de mieux revenir. J’ai repris la COP dès que je suis arrivé chez Deezer — j’ai beaucoup d’appétence pour le partage, etc. donc je m’étais impliqué rapidement dans la COP. Puis, on a eu une grosse phase avec le Covid, surtout — je pense que c’est cette période-là. Covid plus on a passé la COP en anglais, il y a eu un gros ralentissement de la COP. Et puis, je l’ai reprise avec des idées fraîches. Comme j’avais les mains libres, j’ai pu faire un petit peu ce que je voulais. Je m’en suis servi comme d’un laboratoire, parfois un peu à la méthode “à l’arrache” ! J’ai testé plein de trucs, ça n’a pas trop mal marché, et puis là, je pense qu’on arrive à la fin d’un cycle. Il va falloir retrouver des idées, retrouver du sponsoring, retrouver aussi un cercle d’excellence, qui vont être des personnes motrices pour une COP. Il faut ce cercle d’excellence. Par exemple, pour Souhail, ça va être, pour le moment, les Leads. Chez nous, ça va être peut-être des QA Senior ou des gens qui sont très intéressés par la communauté du test, par exemple. Donc on va utiliser ces personnes-là pour avoir de nouvelles idées, pour présenter des choses et re-dynamiser cette COP.</p><p><strong>[00:41:44.660] — Loic </strong>: J’ai une question sur l’accessibilité de la COP et notamment la diversité, le temps de parole, etc. Est-ce que vous avez des problèmes de diversité dans les COP ? Ça peut être des diversités de profils, ça peut être des diversités au sens vraiment large — un truc tout bête, est-ce que vous avez des femmes dans vos COP, par exemple ? Et comment gérez-vous le temps de parole, notamment l’équité, l’égalité hommes-femmes ou ce genre de choses ? Sachant que, comme partout, il y a toujours des gens qui veulent parler plus que d’autres.</p><p><strong>[00:42:17.020] — Souhail :</strong> Moi, j’ai de la chance d’avoir de la diversité dans la team et je pense que les seules femmes qui sont dans la COP sont dans mon équipe. J’espère que je n’oublie personne, mais de souvenir, non !</p><p><strong>[00:42:29.310] — Loic :</strong> Sinon, on va avoir des gens qui vont faire la grève dehors.</p><p><strong>[00:42:32.150] — Souhail :</strong> Oui, je vais avoir des problèmes ! Aujourd’hui, à la COP Front, je n’ai jamais constaté de problématique liée au temps de parole entre hommes et femmes. Ça se fait dans le respect et pour moi, c’est agréable.</p><p><strong>[00:42:46.710] — Nicolas :</strong> Nous, on ne va pas mesurer le temps de parole hommes-femmes ou des équipes. On va vraiment privilégier les personnes qui s’intéressent et qui s’impliquent. On a de la chance, on deux équipes qui sont plutôt impliquées, avec des profils forts — on va dire — que ce soit hommes et femmes. Le temps de parole par équipe, c’est beaucoup plus compliqué. On va essayer de chercher les équipes qui proposent moins de sujets, par exemple. C’est le plus difficile. On va avoir une disparité, on a des équipes qui sont plus à même de proposer des sujets et de présenter que d’autres. Du coup, ces autres équipes, il faut essayer d’aller les chercher.</p><p><strong>[00:43:33.550] — Denis : </strong>De mon côté, on n’a peut-être pas autant de profils féminins. Du coup, je vais parler plus en termes de temps de parole et d’égalité entre les différents points de vue et les différentes équipes. Ce que je constate, c’est qu’effectivement, il y a des grands bavards — ça m’arrive d’en faire partie, d’ailleurs. C’est le rôle du facilitateur — alors, je multiplie un petit peu les casquettes autour de la COP SRE malheureusement, il faudrait que je délègue un petit peu plus. Mais c’est aussi le rôle du facilitateur de s’assurer que tout le monde peut exprimer son point de vue, de peut-être dire à ceux qui parlent un petit peu trop d’écouter un petit peu ce que disent les autres, d’aller chercher les gens qui ne parlent pas trop et leur dire “et toi, qu’est-ce que tu en penses ?” Il faut qu’il y ait un facilitateur. Même si on est tous égaux au sein de la COP, il y a quand même une personne qui présente, potentiellement — s’il y a une présentation — et surtout, il faut un facilitateur qui va justement faciliter les échanges et s’assurer que tout le monde est capable de parler à égalité.</p><p><strong>[00:44:37.160] — Vincent : </strong>J’imagine qu’en plus, ce n’est pas qu’une question de grand parleur, pas beaucoup parleur. Il y a peut-être aussi une question de séniorité. Parfois, on se sent plus légitime que d’autres sur certains sujets. Est-ce qu’on arrive parfois à aller chercher les juniors pour qu’ils viennent donner leur opinion, qui est aussi importante que celle des gens qui ont plus de bouteille ?</p><p><strong>[00:44:57.530] — Souhail :</strong> Oui, je pense qu’on peut trouver des personnes qui sont un peu plus juniors que d’autres, dont le temps de parole se limite à poser une question, à faire une petite remarque, sans plus. À la COP Front, aujourd’hui, c’est le rôle de facilitateur, qui est le “Zizou” chez nous, qui essaye de faire attention à ce genre de biais et de justement s’assurer que tout le monde ait le bon temps de parole, que ça se fasse dans le respect et que tout le monde puisse s’exprimer librement et poser les questions, même si parfois certaines personnes les qualifient de “bêtes”. Pour moi, il n’y a pas de question bête. Et justement, c’est là où parfois, on s’aperçoit qu’autour de la table, tout le monde est “bête” parce qu’ils ont peur et n’ont pas osé poser les bonnes questions.</p><p><strong>[00:45:47.350] — Nicolas :</strong> On essaye de demander si tout le monde a bien compris, si tout le monde sait ce que c’est. Par exemple, quand on parle d’un terme très spécifique à une équipe, on va interpeller les gens pour vraiment aller les chercher quand on voit qu’il n’y a pas de réactions, qu’on est quasi sûr qu’ils ne comprennent pas. Donc on va essayer de les chercher.</p><p><strong>[00:46:10.480] — Vincent : </strong>Cette question, je la pose parce que j’ai vécu un truc comme ça où en pleine rétro, on essayait de comprendre pourquoi la COP avait un problème d’érosion de public, elle ronronnait. Et là, truc tout bête, il y avait une junior — qui était alternante de mémoire — qui prend la parole et qui nous dit : “Vous savez, des fois, vous parlez, vous êtes dans vos termes techniques que vous connaissez tous dans la boite, mais moi, je viens d’arriver et vous me parlez d’un ARL”, par exemple — qui est un token d’auto-relogging. En fait, les gens qui viennent d’arriver — et quand je dis “junior”, ça peut aussi juste être des gens qui viennent de rentrer dans la société — ne comprennent pas, donc ne participent pas et donc prennent du recul et finalement, ne viennent pas.</p><p><strong>[00:46:58.060] — Loic :</strong> Oui, le jargon peut vite être une barrière.</p><p><strong>[00:47:00.190] — Souhail :</strong> Je confirme !</p><p><strong>[00:47:03.870] — Loic :</strong> J’ai peut-être une toute dernière question. Est-ce que vous auriez des conseils pour qui voudrait monter une COP ? Et j’ajouterais, pour qui voudrait participer à une COP ?</p><p><strong>[00:47:14.870] — Nicolas :</strong> Premier conseil: ne pas forcer les gens. Avoir et se concentrer, ou se reposer, sur ce qu’on va appeler un cercle d’excellence. Ce sont des gens qui vont être intéressés, motivés, passionnés par le sujet autour duquel est positionnée la COP. Je pense que ça, c’est le point primordial.</p><p><strong>[00:47:39.810] — Denis :</strong> Faites des blagues. Je me prends peut-être pas assez au sérieux, je ne sais pas, mais je considère que pour une COP, il faut vraiment que ce soit un cadre détendu. Et je ne sais pas faire des cadres détendus sans des blagues, donc je fais des blagues.</p><p><strong>[00:47:55.370] — Loic : </strong>Peu importe, mais il faut que ce soit détendu.</p><p><strong>[00:47:57.400] — Denis :</strong> Voilà !</p><p><strong>[00:47:58.490] — Souhail :</strong> Je me permets d’ajouter : une fois que l’on a le sens de la COP, le pourquoi du comment et que l’on a expliqué à tout le monde pourquoi on va les réunir toutes les semaines ou tous les 15 jours pour leur faire des présentations “chiantes”, il faut leur expliquer pourquoi. Ensuite, il faut trouver des alliés qui vont permettre d’aider la personne à animer la COP, qui vont donner des idées pour que ça se rafraîchisse, pour qu’il y ait une émulation d’idées. Ensuite, la sécurité psychologique est importante pour que tout le monde soit libre de s’exprimer, de partager, de poser des questions. Ensuite, le côté fun, je trouve que c’est très intéressant. Enfin, l’excellence, c’est parfait dans le sens où il faut que cette COP, elle ait un vrai impact, qu’on puisse le voir et qu’on puisse le…</p><p><strong>[00:48:55.350] — Denis : </strong>Le quantifier.</p><p><strong>[00:48:55.680] — Souhail :</strong> Merci.</p><p><strong>[00:48:56.390] — Nicolas : </strong>Et ne pas hésiter aussi à sortir un peu de l’opérationnel sur certains sujets et de sortir un petit peu de son cadre de temps en temps. Par exemple, si vous avez plusieurs COP dans votre entreprise — comme c’est le cas chez nous, faire des échanges entre COP, de la “cross-COP”.</p><p><strong>[00:49:17.180] — Denis : </strong>Des crossovers.</p><p><strong>[00:49:18.040] — Nicolas :</strong> Des crossovers ! Et surtout, ne pas hésiter à sortir un sujet qui est peut-être un peu plus métier — métier dans le sens profession.</p><p><strong>[00:49:29.890] — Vincent :</strong> Je crois que ça va être là-dessus que l’on va s’arrêter pour cette première partie. Merci à tous les trois pour toutes ces informations et toutes ces expériences que vous avez partagées avec nous. Je vous propose de passer à la deuxième partie du podcast, qui est votre recommandation musicale.</p><p><strong>[00:49:53.280] — Denis :</strong> Alors moi, j’ai lancé la track sans faire exprès tout à l’heure. C’est “The Last Imagination”. C’est le nouveau single de Dark Tranquillity, qui annonce le prochain album qui arrive bientôt. Dark Tranquillity, mon groupe préféré depuis que je suis grand ado et dont je ne me lasse jamais. C’est du death metal symphonique <em>(note: erratum, c’est en fait du death metal mélodique)</em>.</p><p><strong>[00:50:13.080] — Souhail : </strong>Je reste à peu près sur le même thème. Il y a une chanson que j’écoute en boucle depuis des jours, c’est Deep Purple, “Child in Time”. Je vous la conseille. C’est trop bien pour se détendre.</p><p><strong>[00:50:25.640] — Nicolas : </strong>Je vais faire deux recommandations. La première, c’est de la musique. C’est “Running in the Night” de FM-84. C’est une chanson qui m’a permis de découvrir la synthwave. Super genre musical ! Et je vais vous faire une recommandation de podcast pour les personnes qui aiment l’histoire autour de la musique, notamment autour de Mylène Farmer. Il y a un podcast qui s’appelle “Histoire de…”. C’est fait par des passionnés qui retracent énormément d’anecdotes autour de sa musique, toutes les similitudes entre différentes périodes, différentes chansons… C’est un super travail journalistique autour de l’œuvre de Mylène Farmer. Donc je vous le recommande.</p><p><strong>[00:51:07.730] — Loic : </strong>Du coup, aujourd’hui — parce qu’il faut que j’en trouve un à chaque fois — je vais recommander l’artiste Thylacine, qui est un artiste français. Je crois qu’il est français, mais il a d’autres origines aussi — je pense qu’il a plusieurs passeports, mais peu importe ! Il fait de la musique électronique avec beaucoup d’inspirations de pas mal d’endroits dans le monde, il utilise beaucoup d’instruments acoustiques. Et dernièrement, son dernier projet — que je suis allé voir en concert, notamment — s’appelle “Thylacine and 74 musicians”. En fait, il a globalement un orchestre symphonique et un chœur avec lui, et il revisite des classiques mais à sa sauce, avec ses genres, ses styles et l’électronique qu’il ramène. C’est un mélange entre la partie purement classique que l’on pourrait retrouver — du Ravel, etc. — et de la musique électronique avec de la musique du monde, je dirais. Vraiment intéressant !</p><p><strong>[00:52:02.680] — Vincent :</strong> Moi, j’allais proposer quelque chose mais la suggestion de Nicolas me fait penser à autre chose, donc je vais vous proposer autre chose. Je vais parler de Toxic Avengers, son album “Yes Future”, et particulièrement la track “Bien cordialement”. Je suis toujours très gêné, je ne sais pas si je dois en rire ou en pleurer, mais je trouve que cette track est vraiment magnifique. Voilà !</p><p><strong>[00:52:23.170] — Loic : </strong>Eh bien, je crois que l’on peut mettre le clap de fin.</p><p><strong>[00:52:25.180] — Vincent :</strong> Merci à tous !</p><p><strong>[00:52:27.920] — Souhail :</strong> Merci !</p><p><strong>[00:52:28.340] — Nicolas :</strong> Merci !</p><p><strong>[00:52:28.760] — Denis :</strong> Merci beaucoup.</p><p><strong>[00:52:29.890] — Vincent :</strong> Vous venez d’écouter un épisode de Deez is la tech et nous espérons que vous avez passé un bon moment en notre compagnie. N’hésitez pas à nous attribuer quelques étoiles si votre application de podcast le permet, et à nous faire part de vos retours via les réseaux sociaux et notre compte @DeezerDevs. Ceux-ci nous aideront à améliorer notre contenu afin de le rendre plus utile, enrichissant et plaisant à écouter. Enfin, n’oubliez pas que toutes les transcriptions de nos épisodes, ainsi que les coups de cœur de nos invités, sont disponibles sur notre blog deezer.io. À très vite pour un nouvel épisode et d’ici là, ne pétez ni les plombs, ni les crons !</p><p><strong>[00:53:09.850] — Nicolas :</strong> J’avais une dernière blague qu’on m’a soufflée. Comme on parlait de COP et de remote, on peut littéralement dire : “On ramène la COP à la maison”.</p><p><strong>[00:53:19.130] — Vincent :</strong> C’est fait ! Voilà !</p><p><strong>[00:53:21.560] — Loic :</strong> Moi, je dis : ça peut aller dans l’introduction.</p><p><strong>[00:53:23.200] — Vincent :</strong> Je voulais essayer de faire une COP sans qu’on fasse “good COP, bad COP”…</p><p><strong>[00:53:28.280] — Nicolas :</strong> On me l’a soufflée aussi !</p><h3>Références</h3><ul><li>Communauté de pratique. <em>Wikipédia.</em> <a href="https://fr.wikipedia.org/wiki/Communaut%C3%A9_de_pratique">https://fr.wikipedia.org/wiki/Communaut%C3%A9_de_pratique</a></li><li>S. Housni, V. Di Massa, M. Collart &amp; J. Parmentier (2021, 22 avril). <em>Qu’est-ce qu’une CoP ? </em>Teach Transition.<em> </em><a href="https://teachtransition.eu/quest-ce-quune-cop/">https://teachtransition.eu/quest-ce-quune-cop/</a></li><li>React : <a href="https://react.dev/">https://react.dev/</a> <br>Conferences React : <a href="https://react.dev/community/conferences">https://react.dev/community/conferences</a></li><li>Trello : <a href="https://trello.com/fr">https://trello.com/fr</a></li><li>Chakra UI : <a href="https://v2.chakra-ui.com/">https://v2.chakra-ui.com/</a></li><li>D. Logan, H. Fischer-Wright &amp; J. King (2013). <em>Managez votre tribu</em>. LEDUC.S.</li><li>Jira : <a href="https://www.atlassian.com/fr/software/jira">https://www.atlassian.com/fr/software/jira</a></li><li>Xray : <a href="https://www.getxray.app/">https://www.getxray.app/</a></li><li>Coups de coeur :<br>- <em>“</em><a href="https://deezer.page.link/wpCj39vX4A3Ch5wG6"><em>The Last Imagination</em></a><em>”</em> de Dark Tranquillity<br>- <em>“</em><a href="https://deezer.page.link/oW2vCgtZowrxnvEq6"><em>Child in Time</em></a><em>”</em> de Deep Purple<br>- <em>“</em><a href="https://deezer.page.link/kut7ASrA3FLZo9q17"><em>Running in the Night</em></a><em>”</em> de FM-84<br>- “<a href="https://deezer.page.link/Z6cAN7bgqwwByAUk6">Histoire de…</a>”, le premier podcast entièrement consacré à Mylène Farmer<br>- <a href="https://deezer.page.link/kx3ZNocEgratFrsU8">Thylacine</a>, notamment l’album <a href="https://deezer.page.link/3CyTVorJMzCe7Rt3A">“...and 74 musicians”</a><br>- “<a href="https://deezer.page.link/FZB4iE4C92QRrj58A">Yes Future</a>” de The Toxic Avenger, notamment le morceau <em>“</em><a href="https://deezer.page.link/Dkr5LUYpVscQ9P4G7"><em>Bien cordialement</em></a><em>”</em></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dea6047126fa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deezer-engineering/dilt-s02e06-good-cop-bad-cop-comment-organiser-et-animer-une-communaut%C3%A9-de-pratique-dea6047126fa">DILT — S02E06 — Good COP, bad COP : comment organiser et animer une Communauté de Pratique</a> was originally published in <a href="https://medium.com/deezer-engineering">Deezer I/O</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>