<?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[JOOR Engineering - Medium]]></title>
        <description><![CDATA[Blog for the Engineering team at JOOR http://joor.com - Medium]]></description>
        <link>https://medium.com/joor-engineering?source=rss----56e242dfb38---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>JOOR Engineering - Medium</title>
            <link>https://medium.com/joor-engineering?source=rss----56e242dfb38---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 03 Jun 2026 02:49:29 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/joor-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[Harnessing the Wind: Why Humans Are Now the Productivity Tool for AI]]></title>
            <link>https://medium.com/joor-engineering/harnessing-the-wind-why-humans-are-now-the-productivity-tool-for-ai-66f6643ed593?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/66f6643ed593</guid>
            <category><![CDATA[agentic-ai]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[agents]]></category>
            <dc:creator><![CDATA[James Tate]]></dc:creator>
            <pubDate>Wed, 01 Apr 2026 15:53:17 GMT</pubDate>
            <atom:updated>2026-04-01T15:53:17.239Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E1URYgmZiORsa1HzK0V7Qg.png" /></figure><p>The ancient Phoenicians were very skilled sailors. They navigated the seas with ease and mastery and their ships depended to a large part on the effort of many rowers. The ships did have a sail, but it was underutilized. By and large power was derived from human labor. The Phoenicians were confident they could navigate any sea and sail from point to point predictably. But it wasn’t the fastest way to travel.</p><p>Fast forward to today where we have super sleek and hyper-engineered sailboats such as the AC75 pictured above, that seem to levitate over water as they hurtle across the sea. Humans are no longer on board to provide the raw power to move the ship but they are there to guide the ship effectively. The difference is that modern ship design is primarily focused on sails, hulls and materials. How to harness the wind efficiently and consistently is the main differentiator.</p><p>Software engineering is undergoing this exact transition. AI is no longer a supplementary “sail” to human typing. AI is the wind. It is the main power source, and as Steve Ballmer recently noted, the dynamic has inverted: <strong>humans are now a productivity tool for AI</strong>.</p><h3>With great power comes great responsibility</h3><p>This<strong> </strong>quote from Spider-Man is apt in the world of software development. Because as AI models become more powerful and AI agents act with greater autonomy it is incumbent on the human engineers to provide a level of governance to ensure that AI output properly reflects the design philosophy of your team and is guided by context and taste. Here’s an example that illustrates outcomes that can happen with unchecked AI: I attended a meetup a few weeks ago with CTOs and Product Managers. One topic of conversation revolved around the best ways to adopt AI coding assistants for your team. One person told a story about a junior engineer on her team who felt like he was falling behind in utilizing AI. So he started using an agent to refactor a gnarly piece of code. However, the agent over-engineered a solution and created a pull request that was 7000 lines long! The CTO looked over this code and said “this doesn’t make any sense. This is overkill. I can see that an AI created it. “</p><p>The junior engineer didn’t even know that a 7000 line pull request was not the best solution. That’s the pitfall of turning the reins over to an AI agent if <strong><em>you are not really in control. </em></strong>Because working with one agent puts that agent in control. But with the introduction of many, you remain in the driver’s seat and maintain your judgement. There is also the danger of hallucinations. Hallucinations are false but plausible sounding responses that AI models give because of training issues, gaps in source data or pattern-matching errors. Open-AI recently touted that GPT‑5’s responses are ~45% less likely to contain a factual error than GPT‑4o. This is great but when we’re developing software code we need to ensure that the accuracy of the solution is 99.99% (Four 9s) and not 85% (or a B+).</p><h3>Designing Agentic Systems with Trust built-in</h3><p>Given the potential pitfalls with handing over the reins of development to an AI assistant how can you build trust into your system. Interacting with an AI agent can feel like collaborating with a junior intern who is being managed as a senior developer; in reality, they often resemble a recent college graduate who possesses a misplaced sense of absolute expertise.We are moving from spec-driven development to ‘vibe coding’ and rapid, agent-assisted prototyping.</p><p>But the reality is this: <strong>Speed without governance is simply liability</strong>. To build trust, we have to move away from “blind faith” and toward architectural guardrails. (See this article on <a href="https://martinfowler.com/articles/exploring-gen-ai/harness-engineering.html">Harness Engineering</a>) One way would be to use the tried-and-true method of Human-In-The-Loop verification, or the newer term <a href="https://www.elementum.ai/blog/human-in-the-loop-vs-human-on-the-loop">“Human-On-The-Loop”</a>. This can be applied to spot-check a percentage of AI decisions, however, this approach doesn’t scale well and could lead to subjective outcomes when the humans in the loop use different criteria to vet outcomes.</p><p>Another approach which is gaining traction is the multiple-agent approach. In this paradigm you use agents trained for specific tasks such as requirements gathering, code development, QA, UX verification, etc. and a team of other agents to “check their homework” This approach helps to reduce overall risk because no single agent is responsible for the outcome but instead a team of agents work together to ensure quality and build overall trust into the system.</p><h3>Charting the path forward</h3><p>For the past two years, every boardroom discussion has focused on the same question: <em>How can AI be a productivity tool for humans?</em>” But as Steve Ballmer recently observed, that view is already backward. We are rapidly entering an era where humans are becoming a productivity tool for AI. Think about it. AI has the speed of execution and the breadth of knowledge. What it lacks is context, judgment, and taste.</p><p><strong>In this new paradigm, the AI acts as the engine, and the human role shrinks — but intensifies — into serving as the steering wheel.</strong></p><p>So as we race at the speed of light towards a future with more and more AI, don’t forget that you are the driver and the steering wheel is yours.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=66f6643ed593" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/harnessing-the-wind-why-humans-are-now-the-productivity-tool-for-ai-66f6643ed593">Harnessing the Wind: Why Humans Are Now the Productivity Tool for AI</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Swift — Understanding the “any” and “some” keywords]]></title>
            <link>https://medium.com/joor-engineering/swift-understanding-the-any-and-some-keywords-42d8db4cea16?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/42d8db4cea16</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios-development]]></category>
            <dc:creator><![CDATA[Eneko Díaz Romero]]></dc:creator>
            <pubDate>Mon, 07 Jul 2025 13:00:22 GMT</pubDate>
            <atom:updated>2025-07-07T13:00:21.951Z</atom:updated>
            <content:encoded><![CDATA[<h3>Swift — Understanding the “any” and “some” keywords</h3><p>When SwiftUI was introduced, it fundamentally changed the way developers think about building views.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AIFKnf4_pv7dqQ_haqW6Qg.jpeg" /></figure><p>Every view in SwiftUI is now a struct that must conform to the View protocol. To satisfy this requirement, a computed property called body must be implemented, which returns a value of type some View.</p><pre>import SwiftUI<br><br>struct ContentView: View {<br>    var body: some View {<br>        // ...<br>    }<br>}</pre><p>For many developers, this was the first time they encountered the some keyword — but what does it actually mean?</p><h3>The “Some” keyword</h3><p>The some keyword tells the compiler that a value conforms to a protocol but <strong>hides its exact type</strong>, while still guaranteeing that it’s always the <strong>same concrete type</strong>.</p><h4>Hides the type</h4><p>In this example, we’re returning a Text , which conforms to the View protocol, so the compiler accepts the result:</p><pre>struct ContentView: View {<br>    var body: some View {<br>        Text(&quot;Hello, world!&quot;)<br>    }<br>}</pre><p>Outside the view’s implementation, it’s impossible to know the concrete type used to build the view. In other words, the view’s type is <strong>opaque</strong>.</p><pre>let view = ContentView()<br>let body = view.body<br>print(type(of: body)) // &lt;- prints &quot;AnyView&quot; instead of &quot;Text&quot;</pre><h4>Ensures the same concrete type</h4><p>To explain this point, let’s briefly look at a different example. Imagine we want to create an app that lets users manage all their consoles. Let’s start by defining what a Console can do:</p><pre>protocol Console {<br>    func turnOn()<br>}</pre><p>Now, let’s create some concrete console types that conform to that protocol:</p><pre>class PlayStation: Console {<br>    func turnOn() {<br>        print(&quot;PlayStation is turned on&quot;)<br>    }<br>}<br><br>class Xbox: Console {<br>    func turnOn() {<br>        print(&quot;Xbox is turned on&quot;)<br>    }<br>}</pre><p>Great 🎉 Now we need a way to create instances of each console. We might want to return different consoles depending on the brand. The pattern I’m using here is called the <em>factory pattern</em>:</p><pre>enum ConsoleManufacturer {<br>    case sony<br>    case microsoft<br>}<br><br>class ConsoleFactory {<br>    func createConsole(manufacturer: ConsoleManufacturer) -&gt; Console {<br>        switch manufacturer {<br>            case .sony: PlayStation()<br>            case .microsoft: Xbox()<br>        }<br>    }<br>}</pre><p>Perfect — now everything is set up. If we wanted to create a new console and turn it on, we’d just need to do the following:</p><pre>let factory = ConsoleFactory()<br>let console = factory.createConsole(manufacturer: .sony)<br>console.turnOn()</pre><p>But what would happen if we used the some keyword in the method’s return type?</p><pre>class ConsoleFactory {<br>    func createConsole(manufacturer: ConsoleManufacturer) -&gt; some Console {<br>        switch manufacturer {<br>            case .sony: PlayStation()<br>            case .microsoft: Xbox()<br>        }<br>    }<br>}</pre><p>The compiler would find the following error:</p><blockquote>Branches have mismatching types ‘PlayStation’ and ‘Xbox’</blockquote><p>By using the some keyword, we’d be telling the compiler that we’re returning a concrete — but unspecified — type that conforms to the Console protocol, and that this concrete type <strong>will always be the same</strong>. However, in our method’s implementation, we’re returning different types depending on the manufacturer parameter — which creates a contradiction the compiler can’t accept.</p><h3>The “any” keyword</h3><p>Now let’s take a closer look at the ConsoleFactory method. You’ll notice that its return type is Console, but there’s a problem: Console is a protocol, not a class. A very interesting question at this point would be:</p><blockquote><em>Can a method return a protocol?</em></blockquote><p>The answer is <strong>no</strong> — a method can’t return a bare protocol type. So what’s happening here? Why isn’t the compiler showing any errors?</p><p>This is because in <strong>Swift 5.7 and later</strong>, the compiler <em>implicitly treats</em> a protocol return type like Console as any Console, <strong>for backward compatibility</strong>.</p><p>This method:</p><pre>func createConsole(manufacturer: ConsoleManufacturer) -&gt; Console</pre><p>Is automatically converted to this by default at compile time:</p><pre>func createConsole(manufacturer: ConsoleManufacturer) -&gt; any Console</pre><p>In this case, the any keyword is hiding the exact type of the returned value. It <strong>doesn’t require all return values to be of the same concrete type</strong> — it only ensures that they all conform to the Console protocol.</p><h3>Conclusion</h3><p>Use some when you want to return a specific type that conforms to a protocol, but don’t want to expose its exact type. It ensures consistency and better performance. Use any when you need flexibility to return or store multiple types conforming to the same protocol. The right choice depends on whether you value type safety or flexibility in your design.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=42d8db4cea16" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/swift-understanding-the-any-and-some-keywords-42d8db4cea16">Swift — Understanding the “any” and “some” keywords</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting back to basics: Ownership]]></title>
            <link>https://medium.com/joor-engineering/getting-back-to-basics-ownership-f015e3c0bcff?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/f015e3c0bcff</guid>
            <category><![CDATA[engineering-mangement]]></category>
            <category><![CDATA[motivation]]></category>
            <category><![CDATA[ownership]]></category>
            <dc:creator><![CDATA[Ivan Arrizabalaga Getino]]></dc:creator>
            <pubDate>Mon, 17 Feb 2025 17:12:13 GMT</pubDate>
            <atom:updated>2025-02-21T13:32:42.652Z</atom:updated>
            <content:encoded><![CDATA[<p>I still play basketball two or three times a week.</p><p>At this stage, most of us on the court are dealing with old injuries and aging bodies, but we keep playing. Why?</p><p>Because we love it, and because we take ownership — not just of our game, but of everything that comes with it.</p><p>Yesterday morning, as I was cleaning my basketball, I realized how deeply that mindset extends beyond the court. Ownership is why I still play. It’s also why we care so much about the systems we build at <strong>JOOR</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5eB4CLkg310YwM0gMXD_sA.jpeg" /><figcaption>Who would have thought you could clean a basketball?</figcaption></figure><p>The past few weeks have been challenging for our team.</p><p>With the <strong>first major markets of the year</strong>, platform activity surged — something we welcome, but also something that tests the limits of any system. While we proactively planned for this, we also faced unexpected slowdowns and downtime, and we take full responsibility for that.</p><p>Since January, we have implemented:</p><ul><li><strong>10</strong> significant optimizations in our <strong>application features</strong></li><li><strong>6</strong> improvements in our <strong>APIs and integration</strong> subsystems</li><li><strong>6</strong> upgrades in our <strong>infrastructure</strong></li><li>Additional enhancements to our <strong>monitoring</strong> systems</li><li>and counting …</li></ul><p>If you’re one of our customers and experienced disruptions, we sincerely <strong>apologize</strong>. We know how critical JOOR is to your business, and don’t take that lightly.</p><p>If you’re on our team, you know we’ll stay vigilant. We’ll <strong>continue</strong> to improve performance and stability week after week.</p><p><strong>And now?</strong></p><p>We have identified different opportunities to make JOOR faster and more reliable like:</p><ul><li>Better thresholds and limits on our <strong>alerts</strong> for early detection</li><li>Implementing <strong>data archiving</strong> policies on non-needed data</li><li>Optimizing <strong>scaling</strong> policies on our clusters</li><li>Reviewing <strong>legacy services</strong> with not-so-great queries</li><li>…</li></ul><p>The job is not done, we <strong>own it </strong>— end to end.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f015e3c0bcff" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/getting-back-to-basics-ownership-f015e3c0bcff">Getting back to basics: Ownership</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[FOSDEM 2025 My Review]]></title>
            <link>https://medium.com/joor-engineering/fosdem-2025-my-review-b85119e08416?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/b85119e08416</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[fosdem]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[monitoring]]></category>
            <dc:creator><![CDATA[David Garcia]]></dc:creator>
            <pubDate>Mon, 17 Feb 2025 09:37:37 GMT</pubDate>
            <atom:updated>2025-02-17T09:37:37.853Z</atom:updated>
            <content:encoded><![CDATA[<p>My name is David Garcia, Staff Engineer at JOOR, and a few weeks ago, I had the privilege of attending FOSDEM once again — this time to celebrate the conference’s 25th anniversary! <a href="https://medium.com/joor-engineering/fosdem-2024-my-review-d7a4c7f3c547">As I did last year</a>, I’ll share my key takeaways from the event…</p><h4>What is FOSDEM?</h4><p><strong>FOSDEM</strong> (Free and Open Source Software Developers’ European Meeting) is an annual conference held in Brussels, Belgium, dedicated to <strong>free and open-source software</strong>. The event features various talks covering various topics, including programming languages, security, cloud computing, and embedded systems. FOSDEM is free to attend and is known for its community-driven, <strong>non-commercial atmosphere</strong>.</p><p>From my perspective, it’s a must-attend event every year to <strong>stay informed</strong> about what’s happening in the <strong>open-source world</strong>. It not only helps you stay up to date but also allows you to see how <strong>other engineering</strong> teams <strong>have tackled various challenges</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sQW4TuEYbDGNyVMS2Uywww.jpeg" /><figcaption>FOSDEM opening</figcaption></figure><h4>How to prepare for it?</h4><p>To prepare for FOSDEM, the first thing to consider is <strong>your profile and interests</strong>. In my case, I’m particularly passionate about <strong>backend</strong> technologies and <strong>infrastructure</strong>, so I tend to prioritize those tracks.</p><p>Additionally, as I mentioned last year, to organize myself and decide which tracks to attend and what talks are available in each, I use an excellent Android app called <a href="https://play.google.com/store/apps/details?id=be.digitalia.fosdem&amp;hl=es_419"><strong>FOSDEM Companion</strong></a>.</p><p>Once we’ve decided which tracks we want to attend, we need to check the rooms where they take place. It’s also important to consider that not all rooms are the same size, and the number of attendees for each talk can vary. If there’s a talk you don’t want to miss, I recommend attending the previous session on the same track to secure your spot.</p><h4>My 2025 favourite talks</h4><p>This year, as in previous ones, the tracks I prioritized were <strong>Containers, Monitoring, Postgres, Python</strong>, and a new track they introduced: <strong>Cloud Native Databases</strong>. Based on this my favorite talks:</p><ul><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5036-reusing-postgresql-codebase-in-a-distributed-sql-architecture-yugabytedb-/">Reusing PostgreSQL codebase in a Distributed SQL Architecture (YugabyteDB)</a>: I found this talk really interesting because it explained how they reused PostgreSQL’s code for their new database engine.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4759-migrating-massive-aurora-and-mysql-databases-to-vitess-kubernetes-clusters-with-near-zero-downtime/">Migrating Massive Aurora and MySQL Databases to Vitess Kubernetes Clusters with Near-Zero Downtime</a>: They provide a very visual and instructive explanation of how to perform a database migration. Although they focus on their own service, the general principles are applicable to any migration.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5956-tracing-the-internals-of-a-cloud-native-database/">Tracing the Internals of a Cloud-Native Database</a>In this case, they talk about how the tracing system works within ClickHouse.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4958-distributed-sql-technologies-raft-lsm-trees-time-and-more/">Distributed SQL Technologies: Raft, LSM Trees, Time, and More</a>: What I found most interesting about this talk was its format. They discussed how they tackled various database challenges from the perspective of <strong>TiDB</strong> and <strong>YugabyteDB</strong>.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5743-distributed-databases-essential-or-optional-/">Distributed Databases: Essential or Optional?</a>: A short talk, but since it’s given by Peter Zaitsev, it’s a must-attend.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4146-discovering-the-magic-behind-opentelemetry-instrumentation/">Discovering the Magic Behind OpenTelemetry Instrumentation</a>: Yet another talk about OpenTelemetry, but this time, it was well-explained and very visual.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5502-the-performance-impact-of-auto-instrumentation/">The performance impact of auto-instrumentation</a>: The analysis was very interesting, although the conclusions were a bit strange, and the way the data was presented felt somewhat disorganized.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5028-zero-code-distributed-traces-for-any-programming-language/">Zero-Code Distributed Traces for any programming language</a>: My favorite this year within the monitoring track was a talk about how to add traces to any application using eBPF by embedding certain metadata within the information traveling through TCP/IP.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4603-anatomy-of-table-level-locks-in-postgresql/">Anatomy of Table-Level Locks in PostgreSQL</a>: A super interesting talk about the locks PostgreSQL applies when making schema changes and how we could perform these changes with fewer locks.</li><li><a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4852-how-browsers-really-load-web-pages/">How browsers REALLY load Web pages</a>: It demystifies a lot of what happens every time a browser loads a webpage.</li></ul><p>And these are the talks I would highlight from this year, 2025. I still have a few saved that I haven’t watched yet, most of which haven’t had their videos uploaded, but I check them every day. Honestly, I’m already looking forward to attending again next year!</p><h4><strong>Next steps</strong></h4><p>Attending FOSDEM is a great personal experience, but it doesn’t end there, the real impact comes from what we do next. To maximize the benefits of this experience, we should share key takeaways with our teammates, and encourage discussions about open-source adoptions. Evangelizing open-source principles within our teams is essential to fostering a culture of collaboration, transparency, and innovation.</p><p>We also have to keep trying to attend these types of events. The next one on my radar is the <a href="https://www.opensouthcode.org/conferences/opensouthcode2025">OpenSouthCode</a> held in Malaga. I hope to find you there :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b85119e08416" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/fosdem-2025-my-review-b85119e08416">FOSDEM 2025 My Review</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Slack tips from a gen Z]]></title>
            <link>https://medium.com/joor-engineering/slack-tips-from-a-gen-z-011e3485fd33?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/011e3485fd33</guid>
            <category><![CDATA[communication-skills]]></category>
            <category><![CDATA[tech]]></category>
            <category><![CDATA[gen-z]]></category>
            <category><![CDATA[slack]]></category>
            <dc:creator><![CDATA[Claudia Castañon Ferreiro]]></dc:creator>
            <pubDate>Wed, 27 Nov 2024 17:14:48 GMT</pubDate>
            <atom:updated>2024-11-27T17:14:48.698Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/998/1*euMKxKSpHueM1y87mPt5sg.png" /></figure><p>Of course, a senior engineer can solve 55 LeetCode exercises in a minute, make 475 tests per feature, and recite any O’Reilly book by heart, but today I want to talk about something that I think is as important (if not more) as all those things, that is: <em>communication</em>.</p><p>I’ll be especially focusing on some tips I learned along the way all these past years while working remotely, that I found out (sometimes not in the best of ways…) and I think they could be pretty useful either if you’re new to professional async communication, or if you have been doing it for a long time but want to contrast your learnings :)</p><h3>Channel etiquette</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/563/1*DInQ6y4QDcXbybB3HidlSg.jpeg" /></figure><ul><li>I’ll begin with one of my most recent finds and something that I find quite useful: I think that 99% of people hate when they’re tagged in a thread where a lot of people are discussing and you shouldn’t have been included from the beginning. I propose 2 different solutions for this: <br>- One (👎): Just mute the thread. Go to the three dots and click on ‘Turn off notifications for replies’.<br>- Two (👍): I like to write whatever is going to start a thread in a message, and then write another one tagging the people that could be interested or implicated. That way, whoever feels they need to contribute to the discussion, can write the proper message and if not, just ignore it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*n6xkocwt9XG6ZBiUIf_vnw.png" /></figure><ul><li>Talking about threads… Threads are the best. Use them. Please!!!</li><li>Use carbon copy in messages. In an ideal world, we wouldn’t have that many Slack messages to read, but just in case you do, it takes 5 seconds to tag in a ‘cc:’ at the end of the message all of the people you expect to read your message.</li><li>Along with the threads, I think pins are your best friends! I love pinning important messages in channels! But also, please, don’t forget to unpin them later 😅</li><li>Reactions are a double-edged sword:<br>- They are a really good option to follow up a message instead of writing in a thread ‘ok!’ ‘I agree’ ‘thanks!’<br>- But: I also think that they’re difficult to follow sometimes, as it notifies you in a very subtle way and I think is very easy to lose track of them. So think about how urgently you want your message to be delivered &amp; read. I’ll show you an example later on.</li></ul><h3><strong>Short n sweet</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/540/1*Teeuv_K28UmPrSbgdzJSEg.jpeg" /></figure><ul><li>Brevity is the soul of wit. Nothing new.</li><li>Keep info to a minimum. One of the things where I struggle the most is that, when trying to explain some kind of problem, I tend to give the full context of it. And by full context I mean a lot of things that probably make sense in my mind but it can be superfluous to the recipient, and can even worsen the understanding of the message itself.</li><li><em>please, please, please… </em>try to keep everything in one message. We all have suffered the infamous <em>hello</em>, the even worse <em>Do you have a minute</em>? and several others, where we have to stay put waiting for the other person, seeing the ‘typing…’ while not knowing if they’re going to ask for help or they are going to tell you bad news. PLEASE !!! normalize giving context even if you think the message you want to explain requires a meeting 🙏</li></ul><h3>Being a good teammate</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*vs75Wr9WLyNN10tIQFp3rA.jpeg" /></figure><ul><li>Because… yes!! sometimes meets are the best option!! I’m the #1 fan of async communication, but sometimes a quick meet or huddle can save you from spending a lot of time having a conversation that is leading nowhere.</li><li>About the example, I talked about before: which one do you prefer?</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0fuQSOMUYJW6DWp8EGT7gw.png" /></figure><ul><li>Reactions are good, but there are some cases where I, at least, prefer to have faster and clearer feedback.</li><li>The most important tip from this article: <strong>Better to Over-Communicate Than Under.</strong> It is also the best relationship advice ever.<strong> </strong>I know I said earlier that it is nice to avoid unnecessary information when trying to share a message and this could ‘clash’ with this one, but what I’m thinking when writing this tip is that it is better to leave everything you feel the need to share written somewhere, that just believe that everything is well placed in your head and as long as you know it, everything will be okay (spoiler: ❌)</li><li>Remember that having a conversation in Slack is very different from one in real life. I tend to be a very ironic person and sometimes it is difficult for people to get the tone in real life, so just imagine what somebody can think of a Slack message 😬. I love being witty and writing not the way I’m supposed to but the way I feel my words are expressed the closest to how I speak in real life, but sometimes I have to take a step back and think a little if my message can be misunderstood.</li></ul><h3>Handling personal space</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/499/0*UWtjD3hPycQShOi2.jpg" /></figure><ul><li>First and foremost: Take into account the trust level you have with that person. I know it sounds pretty obvious but it has happened to me many times that people talk to me (privately) making some jokes that are not only not funny but offensive to me 😅</li><li>This leads us to two other tips: The first one is, I LOVE having informal conversations via Slack! Most of my work friends were made that way. I remember the first day I started at Joor everyone was so nice to me via Slack 😊. But sometimes people get the wrong idea and can tell you about their day or their problems when you just want to be focused on your work tasks. This is a two-way work: As the sender, know the time and place. As a recipient, let people cordially know that maybe it is not the time and place to do that.</li><li>The second one: know the correct channel &amp; time to talk about. If I for example want to talk about how good Wicked is, maybe I can use a public channel where interested people can discuss it, instead of just boring my teammate to death. But also it would be nice to do that at a time when the production environment is not down 😬</li><li>And if it was not clear: channels are the best. I’m sure there are a lot of repeated work questions or lost information which its cause was that they were made in private conversations. Even if you think nobody cares about what you’re asking, maybe somebody takes a quick look at your question and three months later can search in that channel for that, saving themselves a lot of time.</li><li>Also, something that it is not necessary at all but my Capricorn brain enjoys a lot: it is okay if I send a message and you cannot reply, but if you happen to read it and just don’t feel like answering at that moment, saying something like ‘I’ll reply to you in 2 hours’ reduces my cognitive load <em>a lot</em>.</li></ul><h3>Make your messages spark joy</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qOK42UBoj27YBlteutuYIQ.png" /></figure><p>This is the point with which I personally struggle the most 😬 I think Tumblr posts damaged my brain forever…<br>But here’s a series of stylistic choices you can use to help people understand your (long) messages better:</p><ul><li>bullet points (the best one)</li><li>numbered lists: at least for questions for me it is a must. I can’t count how many times I asked several questions with bullet points and only got replies to a few of them 😅 but with numbers I think it is easier for the replier to not skip any answer.</li><li>bold and italics: for keywords! I don’t use these ones that much and I think that usually people tend to highlight entire quotes (which kind of defeats its purpose) but highlighting specific words is your best ally 😌</li></ul><h3>New Era</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/736/0*_tGCcmNESjsFk4zg.jpg" /></figure><p>My most controversial takes: this is for everyone who likes to call younger generations snowflakes instead of thinking with a little bit of empathy 😬</p><ul><li>This is a nitpick but I’m 100% sure you don’t need to send me a gigantic GIF that takes almost the whole space of our Slack conversation and is redundant with what we have just talked about. I think most of these types of messages come from a place to be more friendly but I find them quite annoying 😪 I just simply prefer to read the message you’re searching for in Giphy than the gif itself!</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*kkje-eHWi5GtD-ffIp9Rdg.png" /><figcaption>:(</figcaption></figure><ul><li>Same with emojis… I understand that they are kind of important to balance a little the tone of the message but I think that if you communicate in a good way, you don’t even need them! and if you do, I’m 100% you don’t need them to show <em>exactly </em>the same word you have just written before:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/670/1*mlzLDJlM0BzBqDvvlkZIDQ.png" /></figure><ul><li><a href="https://www.pennlive.com/news/2016/06/a_guide_to_punctuation_on_the.html">We don&#39;t need that many periods anymore, you can use a new line.</a> I had long conversations about this, and the problem is not that much about using periods per se, but if somebody doesn’t usually use periods when writing Slack messages and then they use one when they are sharing a message that could or could not have the best intentions… seems a little weird. I don’t think everybody takes this the wrong way but some people sure do:<br>- <a href="https://www.thecut.com/2016/08/why-ending-a-text-with-a-period-makes-you-a-bad-person.html">Linguistics Explains Why Ending a Text With a Period Makes You Seem Like a Monster</a><br>- <a href="https://forge.medium.com/why-using-a-period-at-the-end-of-a-sentence-sounds-so-angry-1bd6bae71ea1">Why Using a Period at the End of a Sentence Sounds So Angry</a><br>- <a href="https://qz.com/1169792/theres-a-reason-using-a-period-in-a-text-message-makes-you-sound-angry">There’s a reason why using a period in a text message makes you sound angry</a> — I liked the part about <em>A different form of sincerity</em></li><li>Take into account the language barrier: As you have for sure noticed in this article English is not my first language, and there are some words that even though we’re writing in the same language, we don’t understand the same way.<br>In Spain when we want to laugh we usually use ‘jaja’, and if somebody writes ‘haha’ it seems more like a mocking laugh than the real sound we make while laughing. Also when Spanish people use ‘lol’, they mostly use it as if it means ‘wtf’, not like a real laugh. And this goes both ways, so if I see somebody from the US writing lol at the end of a sentence, I do my best to understand that they’re really laughing and not wondering what I just say 😅</li><li>The same happens with the memes! I myself love Pepe the Frog. It is cute and I love frogs and there’s a lot of cute pictures you can use as reactions. But <a href="https://www.filmin.es/pelicula/feels-good-man">it has a complicated story</a> and I can understand that some people could take using a reaction the wrong way (although I wish more people knew about the documentary and the artist and could just separate those two things, but as it is not very well known, it is not that much on an effort for me to learn and try not to be disrespectful).</li><li>And that goes along with not taking everything the wrong way and overreacting. I think a lot of the tips mentioned here are things that people may have not thought about or don’t know about. So if you’re in a situation where you’re feeling uncomfortable, we’re all adult people and you can kindly share your opinion and say something like: ‘Hey, I didn’t like that much the way you said that thing to me. did I misunderstand you or is there a real problem we should talk about?’</li></ul><p>And that’s all! don’t take this article too seriously as these are just based on my personal experience and I’m constantly growing and learning how can I improve communication every day! I hope some of them were useful to you :)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/564/1*o4vndgOjyfBr1vYDfLk2MA.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=011e3485fd33" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/slack-tips-from-a-gen-z-011e3485fd33">Slack tips from a gen Z</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How we integrate Zendesk SSO login into JOOR]]></title>
            <link>https://medium.com/joor-engineering/how-we-integrate-zendesk-sso-login-into-joor-4394aeef89d2?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/4394aeef89d2</guid>
            <category><![CDATA[zendesk-guide]]></category>
            <category><![CDATA[sso]]></category>
            <category><![CDATA[zendesk]]></category>
            <category><![CDATA[zendesk-integration]]></category>
            <category><![CDATA[development]]></category>
            <dc:creator><![CDATA[David Garcia]]></dc:creator>
            <pubDate>Wed, 14 Aug 2024 07:54:57 GMT</pubDate>
            <atom:updated>2024-08-14T07:54:57.089Z</atom:updated>
            <content:encoded><![CDATA[<p>Like other companies, JOOR integrates Zendesk to improve customer support efficiency. It provides a unified platform for managing customer inquiries across various channels (email, chat, phone, social media) and helps streamline communication, automate repetitive tasks, and track customer interactions. This enhances the customer experience and allows companies to resolve issues more quickly and effectively.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*or9hWBLW9nXR_X22.png" /></figure><p>In this article, we will explain how we integrated SSO into our platform. Our approach was based on <a href="https://support.zendesk.com/hc/en-us/articles/4408845838874-Enabling-JWT-single-sign-on">this official Zendesk article</a>.</p><h3>Introduction</h3><p>We first need to understand how the login flow with a third-party service works within Zendesk. In this case, the first step is to <a href="https://support.zendesk.com/hc/en-us/articles/4408845838874-Enabling-JWT-single-sign-on#topic_gds_ydj_zj">create a new SSO client within Zendesk</a>. To do this, Zendesk will ask for the URL to which users should be redirected when they select this login option. After completing this setup, Zendesk will provide a key that we must use to sign the tokens that will authenticate users within the Zendesk platform.</p><p>Once we have the key to sign the Zendesk login token, we’ll go through the detailed steps needed t<a href="https://support.zendesk.com/hc/en-us/articles/4408845838874-Enabling-JWT-single-sign-on#topic_xml_kdj_zj">o authenticate a user from our platform in Zendesk</a>:</p><ol><li>A user wants to log in to our Zendesk through our platform</li><li>Zendesk redirects this user to the URL with the path that we indicate in the configuration</li><li>The route within our platform to which Zendesk redirects our user validates whether the user is already logged in within our platform or forces them to log in.</li><li>Once the user has an active session within JOOR, we ask our API to generate a session token for Zendesk using the key that Zendesk provided us.</li><li>Our web portal redirects with this session token to Zendesk</li><li>The user finally accesses Zendesk as a logged-in user</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1ZgGkvzPUjsLKoIQC_Plzw.png" /><figcaption>Login process</figcaption></figure><h3>Implementation details</h3><p>Once we have a clear understanding of the generic process for authenticating users in Zendesk from a third-party service, we can explain how we’ve implemented it within our platform.</p><p>The JOOR platform can be summarized as a Single Page Application (SPA) built with React, along with an API that retrieves the data clients see on each page. Both our web application and API have certain endpoints that are public (access without being logged in) and others that are private (logged-in access only).</p><p>To implement this authentication flow within our platform, we need to create a private endpoint in our API that logged-in users can use to request their Zendesk authentication token. This endpoint must be private and accessible only to registered users. It will return a JWT signed with the secret provided by Zendesk. The structure of the JWT expected by Zendesk is described <a href="https://support.zendesk.com/hc/en-us/articles/4408845838874-Enabling-JWT-single-sign-on#topic_otw_jfh_3fb">here</a>, and an example of the code would be:</p><pre>import jwt<br>import time<br>import uuid<br>import os<br><br>def generate_zendesk_sso_token(logged_user: User) -&gt; str:<br><br>  payload = {<br>      &#39;iat&#39;: int(time.time()),<br>      &#39;jti&#39;: str(uuid.uuid4()),<br>      &#39;external_id&#39;: logged_user.id,<br>      &#39;email&#39;: logged_user.email,<br>      &#39;email_verified&#39;: True,<br>      &#39;name&#39;: logged_user.name,<br>  }<br><br>  zendesk_secret = os.getenv(&#39;ZENDESK_SSO_SECRET&#39;)  <br>  token = jwt.encode(payload, zendesk_secret, algorithm=&#39;HS256&#39;)<br><br>  return token<br></pre><p>Once we have this API endpoint implemented, we move on to our SPA page. In this case, it will be a page on a new path that simply redirects to Zendesk via a POST request. Before performing this POST redirection, it will need to obtain the Zendesk token from the API to authenticate the user.</p><pre>$ # cURL format required by Zendesk<br>$ curl --location &#39;https://&lt;your-zendesk-domain&gt;.zendesk.com/access/jwt&#39; \<br>--header &#39;Content-Type: application/x-www-form-urlencoded&#39; \<br>--data-urlencode &#39;jwt=&lt;user-jwt&gt;&#39; \<br>--data-urlencode &#39;return_to=&lt;return_to_url&gt;&#39;</pre><p>In the official Zendesk article we’ve mentioned several times here, they explain that when Zendesk redirects users for login, they add a query parameter called return_to, which indicates the original path from which our users decided to log in. To improve their browsing experience, it’s highly recommended to send this parameter back to Zendesk, so these users can continue navigating from the point where they left off. With that in mind, our React component would look like this:</p><pre>const ZendeskSSOLoginPage = () =&gt; {<br>  const DEFAULT_ZENDESK_URL = getDefaultZendeskUrl()<br>  const ZENDESK_SSO_URL = getZendeskSSOUrl()<br><br>  const { data, loading } = useFetchSSOToken()<br>  const searchParams = new URLSearchParams(useLocation().search)<br>  const returnTo = searchParams.get(&#39;return_to&#39;) || DEFAULT_ZENDESK_URL<br><br>  const sendPostRequest = (jwt_token: string, returnTo: string) =&gt; {<br>    const form = document.createElement(&#39;form&#39;)<br>    form.method = &#39;POST&#39;<br>    form.action = ZENDESK_SSO_URL<br><br>    const jwtInput = document.createElement(&#39;input&#39;)<br>    jwtInput.type = &#39;hidden&#39;<br>    jwtInput.name = &#39;jwt&#39;<br>    jwtInput.value = encodeURIComponent(jwt_token)<br>    form.appendChild(jwtInput)<br><br>    const returnToInput = document.createElement(&#39;input&#39;)<br>    returnToInput.type = &#39;hidden&#39;<br>    returnToInput.name = &#39;return_to&#39;<br>    returnToInput.value = encodeURIComponent(returnTo)<br>    form.appendChild(returnToInput)<br><br>    document.body.appendChild(form)<br>    form.submit()<br>  }<br>  useEffect(() =&gt; {<br>    if (!loading &amp;&amp; !!data) {<br>      sendPostRequest(data.token, returnTo)<br>    }<br>  }, [data, loading])<br><br><br>  return (<br>    &lt;LoaderComponent&gt;<br>        Redirecting...<br>    &lt;/LoaderComponent&gt;<br>  )<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/581/1*5NdmrJjkRHniQybqZEkr8w.png" /><figcaption>JOOR Zendesk SSO integration workflow</figcaption></figure><h3>Conclusion</h3><p>As seen in the article, integrating our platform with Zendesk to authenticate users has been quite straightforward. If you have any questions or suggestions, please let us know. We hope this article has been helpful to you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4394aeef89d2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/how-we-integrate-zendesk-sso-login-into-joor-4394aeef89d2">How we integrate Zendesk SSO login into JOOR</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting Up a Local AWS S3 Environment for Development: A Step-by-Step Guide]]></title>
            <link>https://medium.com/joor-engineering/setting-up-a-local-aws-s3-environment-for-development-a-step-by-step-guide-573353200d32?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/573353200d32</guid>
            <category><![CDATA[localstack]]></category>
            <category><![CDATA[devex]]></category>
            <category><![CDATA[s3]]></category>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[dev]]></category>
            <dc:creator><![CDATA[David Garcia]]></dc:creator>
            <pubDate>Tue, 09 Apr 2024 08:33:12 GMT</pubDate>
            <atom:updated>2024-04-09T08:50:17.266Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>These days, it’s very common for Software Engineers to create applications that require access to third-party services. Examples of such third-party services include APIs, databases, and cloud-based applications. Many applications and companies operate and deploy their services on Amazon Web Services (AWS). Additionally, one of the first and most renowned services offered by AWS is Simple Storage Service (S3).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oauNPPrSqH6YKQPcLb6Kzg.jpeg" /></figure><p>When Docker entered the realm of development, setting up a local database for development became extremely easy. You could create a docker-compose.yml file where you had both the database and your service defined. This allowed for relatively simple end-to-end testing of certain services locally.</p><p>But what about other third-party services? Do I have to create mocks in my tests? Furthermore, in the case of certain services like S3, operations (both read and write) incur a cost. Would I have to pay every time I want to perform an end-to-end test?</p><p>Each type of third-party service has its way of being simulated. In this case, I’m here to explain how we can simulate S3 locally, enabling us to carry out all the operations that S3 allows without needing to access the AWS cloud.</p><h3>LocalStack</h3><p><a href="https://www.localstack.cloud/">LocalStack</a> is a powerful tool used in software development for creating local environments that replicate various AWS cloud services. It allows developers to emulate the behavior of AWS services, such as S3 (Simple Storage Service), SQS (Simple Queue Service), SNS (Simple Notification Service), DynamoDB (NoSQL database), and many others, on their local machines.</p><p>Essentially, LocalStack provides a local sandbox environment that mimics the AWS cloud infrastructure, enabling developers to test their applications locally without incurring any costs associated with using the actual AWS services. This is particularly useful for development and testing purposes, as it allows developers to experiment with AWS services, develop and debug their applications, and run end-to-end tests without relying on the internet or incurring expenses.</p><p>Moreover, this tool comes neatly packaged in a <a href="https://hub.docker.com/r/localstack/localstack">Docker image</a>, ready to be configured in our typical docker-compose.yml file.</p><p>In this article, we won’t delve deeply into all the options and what can or cannot be done, as LocalStack has very good documentation on its website.</p><h3>Example</h3><p>In this section, we’re going to perform an exercise as generic and simple as possible. For this reason, we’ll create a playground environment with the following requirements:</p><ul><li>We’ll have two containers: a Debian and a LocalStack.</li><li>When we start the playground environment (docker compose up -d), the S3 bucket named ‘<em>david-garcia-medium</em>’ must exist.</li><li>From the Debian container, using AWS CLI, I should be able to perform operations with S3 as if it were the actual AWS itself.</li></ul><p>First of all, let’s display the file structure of our repository.</p><pre>./<br>  playground/<br>    aws/<br>      config<br>      credentials<br>    aws_cli/<br>      awscliv2-amd64.zip<br>      awscliv2-arm64.zip<br>    localstack/<br>      s3.sh<br>  .env.debian<br>  .env.localstack<br>  docker-compose.yml<br>  Dockerfile</pre><p>In the ‘playground’ directory, we have the necessary configuration to make our playground environment work. In this case, we have three subdirectories:</p><ul><li><strong>aws</strong>: which contains the authentication configuration required by AWS CLI to access AWS.</li><li><strong>aws_cli</strong>: where we have the downloaded AWS CLI binaries — in this case, we’ve downloaded both the AMD (for Linux in general) and ARM (for Mac with M1 in general) versions.</li><li><strong>localstack</strong>: with a script, which will be used to configure the startup of the LocalStack container.</li></ul><p>The .env.debian and .env.localstack files are files with declared environment variables that will be mounted to the containers in the docker-compose.yml. They contain the following information</p><pre># .env.debian<br># AWS cli configuration<br>AWS_PROFILE=localstack</pre><p>The .env.debian is very simple, and only contains <a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-using-profiles">the AWS environment variable</a> used by AWS CLI to know what profile must be used by default. If we review the files available in playground/aws path we find the following</p><pre># config<br>[profile localstack]<br>output = json<br>endpoint_url = http://localstack:4566<br>region = us-east-1</pre><pre># credentials<br>[localstack]<br>aws_access_key_id=test<br>aws_secret_access_key=test</pre><p>In the playground/aws/config file, we overwrite the endpoint_url to use our LocalStack as AWS backend instead of the official AWS. In the playground/aws/credentials we configure the credentials to the localstack profile.</p><p>Let’s review the .env.localstack</p><pre># .env.localstack<br><br>SERVICES=s3<br><br># Checker<br>TRIES=30<br><br># Base<br>HOSTNAME_EXTERNAL=localstack<br><br>LOCALSTACK_API=http://localstack:4566<br>LOCALSTACK_HEALTH_ENDPOINT=http://localstack:4566/health<br><br># S3<br>BUCKET_NAMES=david-garcia-medium<br><br># AWS cli configuration<br>AWS_DEFAULT_PROFILE=localstack</pre><p>These environment variables are typically utilized by the script located at playground/localstack/s3.sh. This script serves as a fundamental tool that executes once the LocalStack container is up and running, facilitating the creation of the pre-existing S3 bucket that we require. This script will be mounted in the path /etc/localstack/initi/ready.d and the localstack container will execute when it is ready. More info <a href="https://docs.localstack.cloud/references/init-hooks/">here</a></p><p>Let’s review the docker image with debian, the Dockerfile</p><pre>FROM docker.io/debian:bookworm-slim<br><br># Added TARGETARCH to differenciate between amd64 (Linux and Windows) and arm64 (Mac) when install awscliv2.zip<br>ARG TARGETARCH<br><br># Install zip<br>RUN apt update &amp;&amp; \<br>    apt install -y zip=3.0-13 &amp;&amp; \<br>    rm -rf /var/lib/apt/lists/*<br><br># Install awscli<br>COPY playground/aws_cli/awscliv2-${TARGETARCH}.zip .<br>RUN unzip awscliv2-${TARGETARCH}.zip &amp;&amp; \<br>    ./aws/install &amp;&amp; \<br>    rm -rf awscliv2-${TARGETARCH}.zip aws<br><br># By default, this container does not execute anything; it simply sleeps indefinitely.<br>CMD [&quot;tail&quot;, &quot;-f&quot;, &quot;/dev/null&quot;]</pre><p>In this scenario, the image is quite straightforward, with the only sophisticated aspect being our readiness to build the image for both amd64 and arm64 architectures, utilizing the TARGETARCH Docker ARG. We use the binaries stored in playground/aws_cli instead of downloading them via curl in the Dockerfile to ensure consistent installation of the same version of AWS CLI. This practice guarantees that each time we rebuild the image, we maintain uniformity in the awscli version.</p><p>And finally, we see the docker-compose.yml file with the definition of the services before running a test and verifying that everything works.</p><pre>version: &#39;3.7&#39;<br><br><br>services:<br>  debian:<br>    build:<br>      context: .<br>      dockerfile: Dockerfile<br>    depends_on:<br>      - localstack<br>    env_file:<br>      - .env.debian<br>    volumes:<br>      - ./playground/aws:/root/.aws:ro<br><br><br>  localstack:<br>    image: docker.io/localstack/localstack:3.1<br>    ports:<br>      - &quot;4566:4566&quot;<br>    env_file:<br>      - .env.localstack<br>    volumes:<br>      - ./playground/localstack:/etc/localstack/init/ready.d/:ro<br>      - ./playground/aws:/root/.aws:ro</pre><p>As you can see, there’s nothing unfamiliar here that we haven’t commented on previously. Let’s test it:</p><pre>$ docker compose down -v &amp;&amp; docker compose up -d &amp;&amp; docker compose exec debian bash<br>...<br>root@9698486ac759:/# # List the aws s3 buckets availables<br>root@9698486ac759:/# aws s3 ls  <br>2024-04-02 17:57:15 david-garcia-medium<br>root@9698486ac759:/# # Create a file and upload to S3<br>root@9698486ac759:/# echo &quot;hello medium&quot; &gt; my_file.txt<br>root@9698486ac759:/# aws s3 cp my_file.txt s3://david-garcia-medium<br>upload: ./my_file.txt to s3://david-garcia-medium/my_file.txt   <br>root@9698486ac759:/# aws s3 ls s3://david-garcia-medium<br>2024-04-02 18:02:06         13 my_file.txt<br>root@9698486ac759:/# # Create a new S3 bucket<br>root@9698486ac759:/# aws s3 mb &quot;s3://my-new-bucket&quot;<br>make_bucket: my-new-bucket<br>root@9698486ac759:/# aws s3 ls                     <br>2024-04-02 17:57:15 david-garcia-medium<br>2024-04-02 18:04:20 my-new-bucket<br>root@9698486ac759:/# # you can test other commands!</pre><p><strong>Extra example</strong></p><p>Furthermore, LocalStack provides the files we have uploaded to S3 at the following path: http://localhost:4566/&lt;bucket-name&gt;, and you can download them using the S3 key.</p><pre>$ curl http://localhost:4566/david-garcia-medium<br>&lt;ListBucketResult xmlns=&quot;http://s3.amazonaws.com/doc/2006-03-01/&quot;&gt;<br>  &lt;IsTruncated&gt;false&lt;/IsTruncated&gt;<br>  &lt;Marker/&gt;<br>  &lt;Name&gt;david-garcia-medium&lt;/Name&gt;<br>  &lt;Prefix/&gt;<br>  &lt;MaxKeys&gt;1000&lt;/MaxKeys&gt;<br>  &lt;Contents&gt;<br>    &lt;Key&gt;my_file.txt&lt;/Key&gt;<br>    &lt;ETag&gt;&quot;5047cdd6613d55aa1a3143639a81cf78&quot;&lt;/ETag&gt;<br>    &lt;Owner&gt;<br>      &lt;DisplayName&gt;webfile&lt;/DisplayName&gt;<br>      &lt;ID&gt;75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a&lt;/ID&gt;<br>    &lt;/Owner&gt;<br>    &lt;Size&gt;13&lt;/Size&gt;<br>    &lt;LastModified&gt;2024-04-02T18:02:06.000Z&lt;/LastModified&gt;<br>    &lt;StorageClass&gt;STANDARD&lt;/StorageClass&gt;<br>  &lt;/Contents&gt;<br>&lt;/ListBucketResult&gt;<br><br>$ curl http://localhost:4566/david-garcia-medium/my_file.txt<br>hello medium</pre><p>To conclude, you have all the code available in the following <a href="https://github.com/own3dh2so4/localstack_s3_example">GitHub repository</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=573353200d32" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/setting-up-a-local-aws-s3-environment-for-development-a-step-by-step-guide-573353200d32">Setting Up a Local AWS S3 Environment for Development: A Step-by-Step Guide</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why was I rejected from my JOOR interview?]]></title>
            <link>https://medium.com/joor-engineering/why-was-i-rejected-from-my-joor-interview-f7af1aafa2dc?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/f7af1aafa2dc</guid>
            <category><![CDATA[team-culture]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[hiring]]></category>
            <dc:creator><![CDATA[Ivan Arrizabalaga Getino]]></dc:creator>
            <pubDate>Tue, 19 Mar 2024 17:03:36 GMT</pubDate>
            <atom:updated>2024-03-21T12:53:16.935Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>Let’s say you interview for one of our engineering positions, you feel like you have crushed every little thing, and, yet, … no offer. What happened?</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/513/0*AIXodaa3PScNAKBO.jpg" /></figure><h3>How do we organize our hiring pipeline?</h3><p>We recognise that applying to any position sucks no matter what, that’s why we are transparent with our processes.</p><p>Everything starts with the <strong>job description.</strong></p><p>We try to include some context about us, our expectations, the future type of work, available locations, and, above all, a comprehensive list of perks, including the compensation range for the position, no mystery, we don’t want you to lose your time.</p><p>Once the offer is published, we <strong>review internally</strong> every applicant looking for potential fits. Surprisingly, many of the candidates can’t qualify for the position: non-viable locations, total lack of experience in the role (not talking about tech stacks), a salad of buzzwords with no rhyme, resumes with lots of typos…</p><p>When we find you (😉 a lovely candidate), <a href="https://www.linkedin.com/in/naomi-granshaw-31121550/">Naomi</a> will arrange a super brief <strong>HR intro screening</strong> with you. Don’t be afraid, we ain’t gonna challenge your talent yet, but we need to confirm a few things: a legit interest in the role, you can speak good enough English, and our mutual expectations are aligned. <strong>15mins or less</strong>.</p><p>99% of people will move quickly from the HR call to the <strong>leveling interview</strong>. Our goal here is to discover if the candidate can fill our expectations for the role: experience, knowledge, appetite, and, above all, humanity 😅. <strong>30mins</strong>.</p><p>No kidding, like any other organization, we have our fair share of challenges but we can’t afford to hire a bad teammate regardless of how great (s)he codes.</p><p>One or two days after the leveling interview you receive an invite to our final boss… the (remote) <strong>onsite</strong>.</p><h3>The onsite</h3><p>The last stage consists in a series of 3 interviews that we try to run in a row to close our mutual discovery:</p><ul><li><strong>Culture fit</strong>: An open talk with 1 or 2 future teammates where they try to answer the question “Would you like to work with this person?” <strong>30mins</strong>.</li><li><strong>Technical interview</strong>: A longer session with a couple of other people to do some work together. Mostly consists of a few closed technical questions, a brief open design question, and some pairing on a minimalistic exercise. <strong>1hour</strong>.</li><li>[<strong>Management interview</strong>]: Lastly, depending on the role, we will have a last session with one of the managers to close the process. <strong>30mins</strong>.</li></ul><blockquote>In total a JOOR process for you should no longer than <strong>2h45mins</strong> with no take-home assignment 🚀</blockquote><h3>😴 I crushed it, why no offer?</h3><p>Maybe you:</p><ul><li>Only talked about <strong>frameworks</strong> instead of designs decisions.</li><li>Didn’t <strong>listen</strong> while pairing with your future teammates.</li><li>Coudn’t recognize any <strong>mistake</strong> from your past.</li><li>Didn’t care about the <strong>big picture</strong>.</li><li>Navigated the code with no rhyme or <strong>reason</strong>.</li><li><strong>Copied and pasted</strong> until a miracle happens.</li><li>Didn’t show <strong>curiosity</strong>.</li><li>Didn’t recognize the value of <strong>writing</strong> and reading.</li><li>Have a gigantic <strong>ego</strong>. 🙅</li></ul><p>or … maybe you actually did a very good job on your interview but somebody else did better.</p><p>In the end, the question is not if you are qualified for the role but if you’re the one that, we think, can help us the most.</p><h3>That’s all folks!</h3><p>You might be wondering why anybody should care about the hiring philosophy of a mid-sized scale-up.</p><blockquote>It’s just a testament of our vision for building the right company, with the right values and, above all, <strong>the right people</strong>.</blockquote><p>Neither you as a candidate nor we as a company are perfect. We may surely lose some incredible candidates along the way 🤷‍♂. However, if you’re curious, we often have some open positions here👇</p><p><a href="https://www.joor.com/current-openings">https://www.joor.com/current-openings</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f7af1aafa2dc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/why-was-i-rejected-from-my-joor-interview-f7af1aafa2dc">Why was I rejected from my JOOR interview?</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[FOSDEM 2024 My Review]]></title>
            <link>https://medium.com/joor-engineering/fosdem-2024-my-review-d7a4c7f3c547?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/d7a4c7f3c547</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[postgres]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[fosdem]]></category>
            <dc:creator><![CDATA[David Garcia]]></dc:creator>
            <pubDate>Tue, 20 Feb 2024 11:20:35 GMT</pubDate>
            <atom:updated>2024-02-20T11:20:35.138Z</atom:updated>
            <content:encoded><![CDATA[<p>My name is David Garcia, staff engineer at JOOR, and a few weeks ago I was lucky to attend my second FOSDEM, let’s see what I found there …</p><h3>Why FOSDEM?</h3><p>For me, it’s an incredibly exciting experience to witness the latest developments in open-source projects and to learn about new technologies that have emerged over the past year.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r1INWN4t79Orn1bGTWno9Q.png" /><figcaption>Image from FOSDEM 2024 Highlights 20:13</figcaption></figure><p>I’m a backend engineer with a strong interest in databases, observability, Linux, and programming. In my current role, I work extensively with Python, PostgreSQL, and Kubernetes. Therefore, the most interesting tracks for me were:</p><ul><li><a href="https://fosdem.org/2024/schedule/track/containers/">Containers devroom</a>: Saturday 3 February 2024</li><li><a href="https://fosdem.org/2024/schedule/track/monitoring-observability/">Monitoring &amp; Observability devroom</a>: Sunday 4 February 2024</li><li><a href="https://fosdem.org/2024/schedule/track/postgresql/">PostgreSQL devroom</a>: Sunday 4 February 2024</li><li><a href="https://fosdem.org/2024/schedule/track/python-devroom/">Python Devroom devroom</a>: Sunday 4 February 2024</li></ul><h3>How to prepare for it?</h3><p>I always start by selecting my favourite tracks to choose the talks that I find most interesting there. Once I’ve chosen the talks I’m most interested in from my favourite tracks, I then explore the remaining tracks to see which talks I like.</p><p>An important thing to keep in mind about FOSDEM is that not all rooms where talks are held are equally large, nor are they all in the same building. It’s worth considering that if a talk looks very promising, it may fill up before its scheduled time, and you might end up being unable to attend. Similarly, if it’s in a distant building, you may not make it in time.</p><p>To organise which talks to attend and to know where they are, my recommendation is to use the <a href="https://play.google.com/store/apps/details?id=be.digitalia.fosdem&amp;hl=es_419&amp;gl=US">FOSDEM Companion</a> app. This app not only includes all the tracks with their talks and schedules, but it also provides maps of the university campus to show you where the buildings are located, and within each building, it indicates the location of each room.</p><h3>My 2024 favourite talks</h3><p>With that said, here are my favourite talks:</p><ul><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-1855-forensic-container-checkpointing-and-analysis/">Forensic container checkpointing and analysis (Containers devroom)</a>: a very interesting talk about checkpointing containers. It’s a continuation of <a href="https://archive.fosdem.org/2023/schedule/event/container_kubernetes_criu/">the talk</a> he gave last year.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-2988-introducing-incus/">Introducing Incus (Containers devroom)</a>: It teaches us what Icus (fully-fledged system container and virtual machine manager) is, in addition to providing a live demo, which is always appreciated.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3595-open-food-facts-acting-on-the-health-and-environnemental-impacts-of-the-food-system/">Open Food Facts : Acting on the health and environmental impacts of the food system (Lightning talks)</a>: It shows us how, through an OpenSource community, we can monitor the food we buy at the supermarket.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3669-reinventing-database-exploration-with-azimutt/">Reinventing database exploration with Azimutt (Lightning talks)</a>: In this case, since I’ve been reviewing a very large database in my last month of work, this type of tool would have been very helpful for me.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-2181-passbolt-open-source-password-manager-for-teams/">Passbolt — Open source password manager for teams (Lightning talks)</a>: It teaches us how to have our own password manager, in addition to giving us a sneak peek at some features that will come in the future.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3747-testing-containers-with-python-and-pytest/">Testing Containers with Python and pytest (Lightning talks)</a>: It teaches us about the <a href="https://pypi.org/project/pytest-container/">pytest-container</a> library, a library that I wasn’t familiar with and found super interesting and useful for testing our Docker images.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3543-platform-engineering-for-dummies/">Platform engineering for dummies (Lightning talks)</a>: What is platform engineering? Here you have the detailed answer.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3600-isolation-levels-and-mvcc-in-sql-databases-a-technical-comparative-study/">Isolation Levels and MVCC in SQL Databases: A Technical Comparative Study (PostgreSQL devroom)</a>: A graphical and visual explanation of the different types of isolation levels in database transactions and how PostgreSQL applies them.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3601-reducing-costs-and-improving-performance-with-data-modeling-in-postgres/">Reducing Costs and Improving Performance With Data Modeling in Postgres (PostgreSQL devroom)</a>: It explains how data is stored on disk in PostgreSQL and how the order in which we declare columns in a table matters and can help us save space.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-1921-linux-load-average-and-other-silly-metrics/">Linux load average and other silly metrics (Monitoring &amp; Observability devroom)</a>: It teaches us how to interpret typical metrics of Linux systems, such as load average.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3263-practical-ci-cd-observability-with-opentelemetry/">Practical CI/CD Observability with OpenTelemetry (Monitoring &amp; Observability devroom)</a>: A good practical example of integration with OpenTelemetry.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3499-implementing-distributed-traces-with-ebpf/">Implementing distributed traces with eBPF (Monitoring &amp; Observability devroom)</a>: In this case, they teach us how to leverage the features provided by eBPF to add traces to our applications in order to monitor the entire system.</li><li><a href="https://fosdem.org/2024/schedule/event/fosdem-2024-3604-your-virtual-dba-postgresql-on-kubernetes-using-an-operator-/">Your Virtual DBA (PostgreSQL on Kubernetes using an Operator) (PostgreSQL devroom)</a>: They discuss the role of a DBA and the requirements a Kubernetes Operator must have to deploy a database.</li></ul><p>These are the talks I’ve liked the most so far. I haven’t finished watching all the ones I couldn’t attend in person yet. Additionally, some of them haven’t been uploaded to the website yet, so I still have to wait a bit.</p><p>This year has left me with a good impression, and to be honest, I’m looking forward to attending again next year.</p><h3>About JOOR</h3><p>Founded in 2010, JOOR now services more than 14,000 brands and over 600,000 buyers across 150 countries, processing an incredible $1.7B in wholesale transaction volume each month.</p><p>We have some open positions from time to time, if interested and curious just check them <a href="https://www.joor.com/current-openings">here</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d7a4c7f3c547" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/fosdem-2024-my-review-d7a4c7f3c547">FOSDEM 2024 My Review</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How shift left testing can save you bugs, time, and money]]></title>
            <link>https://medium.com/joor-engineering/how-shift-left-testing-can-save-you-bugs-time-and-money-13448678b2bc?source=rss----56e242dfb38---4</link>
            <guid isPermaLink="false">https://medium.com/p/13448678b2bc</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[code-quality]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[quality-assurance]]></category>
            <dc:creator><![CDATA[Mario Pérez]]></dc:creator>
            <pubDate>Thu, 15 Feb 2024 09:32:41 GMT</pubDate>
            <atom:updated>2024-02-15T09:32:41.350Z</atom:updated>
            <content:encoded><![CDATA[<p>In the world of software development, bugs are completely normal and are part of our daily lives. All engineering teams strive to have as few errors as possible because they have a negative impact on users, and fixing them costs the company time and money.</p><p>Preventing bugs is a complicated task, but we can reduce the number of errors in our code by following best practices such as writing tickets with very detailed descriptions that cover all possible use cases, using <a href="https://medium.com/joor-engineering/agile-software-development-solid-principles-ace7828b9adf">clean code</a> when developing features, or writing robust tests, among other things.</p><h3>Whose responsibility is it to ensure there are no bugs?</h3><p>We could say that the responsibility lies solely with QA (Quality Assurance) engineers (if there are any), but besides being false, it’s unfair because we would be delegating all the responsibility to them.</p><p>In some teams, there is the perception of a QA engineer as a role that ensures no ticket introduces bugs into production. I’ve heard developers say, “I write the code and then the QA tests it.” This shouldn’t be the case.</p><blockquote><strong><em>The quality of the code is a shared responsibility among the entire team.</em></strong></blockquote><h3>When should I start thinking about the bugs that the code might generate?</h3><p>Most bugs are introduced during the development phase, and this happens for different reasons: what if the developer has started working on a ticket with an incorrect task description? What if the acceptance criteria are vague and ambiguous or not even defined?</p><blockquote><strong><em>We must think about bugs from the very moment we create and refine a task. Thinking about them during development is too late.</em></strong></blockquote><p>Let’s suppose we have the following software development lifecycle:</p><ol><li>Requirements</li><li>Design</li><li>Implementation</li><li>Review and QA</li><li>Deploy to production</li></ol><p>If you notice, QA is in the last phase before reaching production, which means that <strong>QA feedback comes very late, and the developer will take that feedback into account in the last phase before deploying to production</strong>. This can lead to an endless conversation between the developer and QA when reviewing the functionality, and <strong>a single task can have multiple back-and-forth exchanges (like a ping-pong match) simply because QA wasn’t involved even before starting the development</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QN6HCe2J-T7IzChQUza3Cw.png" /><figcaption>Software development life cycle without shift left</figcaption></figure><p>Fixing a bug incurs a cost that <a href="https://deepsource.com/blog/exponential-cost-of-fixing-bugs">significantly grows</a> as it progresses through the development stages. <strong>It’s much easier and cheaper to fix a bug in the earlier stages of development than once it’s deployed in production.</strong></p><p>Therefore, not using shift left testing has several implications:</p><ul><li>By not focusing on testing from the first stage, implementation time increases</li><li>With increased implementation time, feedback on the task arrives very late</li><li>If feedback arrives late, the effort and cost of changing the code are higher</li></ul><h3>So, what is shift left testing?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OVQfZyzXam5Zg5rXJ4EYjw.png" /><figcaption>Software development life cycle with shift left</figcaption></figure><p><strong>Shift left testing means focusing on testing from the very beginning when we start thinking about how to approach a task</strong>. We must consider all use cases, both happy paths and corner cases, to ensure that task acceptance criteria are as detailed as possible.</p><h3>Benefits of shift left testing</h3><ol><li><strong>Early bug detection</strong>: by moving testing activities earlier in the development process, <strong>bugs and issues can be identified and addressed sooner</strong>, reducing the cost and effort required for fixing them later in the development lifecycle.</li><li><strong>Improved product quality</strong>: with testing integrated earlier in the software development cycle, the <strong>quality of the software improves</strong> as issues are identified and resolved in earlier stages.</li><li><strong>Faster feedback loop</strong>: developers receive quicker feedback on their code, allowing them to make necessary adjustments promptly, leading to <strong>faster iteration and development cycles</strong>.</li><li><strong>Cost reduction and faster time-to-market</strong>: identifying and fixing issues earlier in the development process <strong>reduces the overall cost of software development by avoiding expensive rework and late-stage bug fixing</strong>.</li><li><strong>Increased collaboration</strong>: shift left testing encourages <strong>collaboration between developers, testers, and other stakeholders</strong> from the outset of the project, fostering a <strong>culture of shared responsibility</strong> and collaboration.</li><li><strong>Customer satisfaction</strong>: by delivering higher quality software with fewer defects, shift left testing ultimately leads to increased customer satisfaction.</li></ol><h3>How to implement shift left testing?</h3><h4>Include testers in the earliest stage possible</h4><p>Including testers in the requirements gathering phase is crucial for successfully implementing shift left, as they provide a detailed perspective on how developments should be tested even before developers start writing code.</p><p>If developers have very detailed acceptance criteria and know exactly how the code should behave and what will be tested, the number of bugs and rework will be drastically reduced.</p><p>However, this also implies an evolution of the tester’s role, typically focused on capturing bugs once development has concluded; to the role of QA itself, oriented towards preventing bugs throughout the entire SDLC. This transition is not always easy, and QAs may need some mentorship when adapting to these new functions in their daily routine.</p><h4>Add very detailed acceptance criteria to your tasks</h4><p>Acceptance criteria are a contract that, if we have done a good job of researching the problem beforehand, should not change during development.</p><p><strong>Making changes to acceptance criteria after development is very expensive</strong>, as it forces the programmer to rewrite already completed code due to incorrect requirements.</p><blockquote><strong><em>Make sure all the team understands the acceptance criteria</em></strong></blockquote><h4>Developers must test their code before sending it to the review stage</h4><p>Multiple bugs and conversations between developers and QA can be avoided by testing the code correctly. <strong>Unit testing is an extremely powerful tool</strong> that can be used to reduce review time and the number of bugs in the code. <strong>Manual testing is expensive and should be avoided</strong>.</p><h4>Continuous feedback</h4><p>Developers should work with QA and the Product Manager (if any) during the implementation stage so they can <strong>provide feedback to the developer as soon as possible</strong>. This way, we reduce the amount of rework and the number of late-stage bugs.</p><h3>Real results</h3><p>At JOOR, we have several teams within the engineering team, and I would like to show you some numbers on <strong>how shift left testing can save time, bugs, and money</strong>.</p><p>I won’t be showing the numbers for all teams and all quarters to simplify, but the rest of the teams have had similar results.</p><h4>Metrics</h4><ul><li><strong>Cycle time</strong>: how long does a story take to be <strong>completed</strong> after <strong>being started</strong></li><li><strong>Dev time</strong>: how long does a story remain <strong>in progress</strong></li><li><strong>Review time</strong>: how long does a story take to be moved from <strong>in-review (code review and QA) </strong>to <strong>merged/deployed</strong></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AU-3TQlFfNTu77SVXX_0PA.png" /><figcaption>Metrics by quarter</figcaption></figure><h4><strong>Shape of work</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0b1oHhIk_maZHR9LVADXJQ.png" /><figcaption>Shape of work</figcaption></figure><h4><strong>Takeaways</strong></h4><p>In Q2, we didn’t work with shift left. The <strong>percentage of bugs we fixed was 10.3%</strong>. As you can see, the <strong>review time was over two days</strong>.</p><p>In Q3, we started using shift left testing, but you know how these things go. <strong>Establishing a new methodology in a team is not easy and takes time for everyone to get used to working with it and start seeing the benefits</strong>.</p><p>Despite this, we managed to <strong>reduce the cycle time by 23 hours</strong> and also the review time by 6 hours.</p><p>In the last quarter, <strong>Q4, we reduced the cycle time to 2 days and 11 hours</strong>, which is a reduction of 1 day and a half compared to 6 months ago. <strong>The review time was reduced to 14 hours, which is very low compared to the initial 2 days and 5 hours</strong>.</p><p>In addition to that, <strong>we were able to complete much more work while reducing the percentage of bugs</strong>.</p><p>As you can see, it doesn’t take more time to program using shift left because the development time is maintained, but the review time is greatly reduced, so <strong>we get more work done in less time and also without adding more bugs</strong>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=13448678b2bc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/joor-engineering/how-shift-left-testing-can-save-you-bugs-time-and-money-13448678b2bc">How shift left testing can save you bugs, time, and money</a> was originally published in <a href="https://medium.com/joor-engineering">JOOR Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>