<?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[Stories by Peeter Tomberg on Medium]]></title>
        <description><![CDATA[Stories by Peeter Tomberg on Medium]]></description>
        <link>https://medium.com/@peeter.tomberg?source=rss-b2f05c1a608a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*4gKaOOAdGDJ5G1QAmEr84A.jpeg</url>
            <title>Stories by Peeter Tomberg on Medium</title>
            <link>https://medium.com/@peeter.tomberg?source=rss-b2f05c1a608a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 18 May 2026 21:15:25 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@peeter.tomberg/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Collaborative Confidence: Empowering Teams for Seamless Deployments and Cross-Departmental Success]]></title>
            <link>https://medium.com/engineering-as-a-service/collaborative-confidence-empowering-teams-for-seamless-deployments-and-cross-departmental-success-41e52169d3c6?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/41e52169d3c6</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[collaboration]]></category>
            <category><![CDATA[releases]]></category>
            <category><![CDATA[deployment]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Mon, 22 May 2023 10:46:50 GMT</pubDate>
            <atom:updated>2023-05-25T08:54:33.630Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pSQHDEtBur2nfYty7laxHw.png" /></figure><p>At the start of any new product, the focus is quickly getting stuff in front of customers. The usual workflow tends to be:</p><ul><li>Write code and push it to some test environment</li><li>Test the code on the test environment</li><li>Push the code in front of real users in a production environment</li></ul><p>As your organization grows, so will your requirements, and this workflow will have some drawbacks.</p><h3>Deployments don’t always go flawlessly.</h3><p>I’ve caused many problems trying to deploy new functionality, and the reason is always something different — missing permissions, missing secrets, services being deployed in the wrong order, etc.</p><p>The bigger the functionality, the bigger the risk that something goes wrong when deploying.</p><h3>Production differs from other environments.</h3><p>The bigger your product, the bigger your production environment. Keeping an exact duplicate environment on staging is not fiscally responsible. The main differences I’ve seen are:</p><ul><li>The data in production differs from the staging environment</li><li>The staging environment runs in a single region, while the production environment has multiple regions</li><li>The staging environment machines are scaled down to a single instance</li><li>The staging environment machines are less powerful than the production ones.</li></ul><p>With these changes in mind, functionality that works on staging might not always work in production.</p><h3>Timing matters</h3><p>With larger organizations, it’s no longer just about shipping code; it’s about collaborating on feature releases with other departments.</p><p>When pushing changes to end users, you have to think about more than just the feature, e.g.,</p><ul><li>Is customer service aware of the functionality and can assist users with questions?</li><li>Is the operational team aware of how the functionality works and how to use it?</li><li>Is the marketing team ready to market the new feature?</li></ul><h3>It’s time to decouple deployments from releasing</h3><p>With all these challenges in front of us, it becomes clear we need to separate pushing code to production and releasing functionality to customers.</p><p><strong>Feature flags</strong> are a great way of achieving this. They are nothing fancy; essentially, they are just if statements around your code.</p><pre>if (featureFlags.isNewRegistartionFlowEnabled) {<br>  // new registration flow<br>} else {<br>  // old registration flow<br>}</pre><p>The feature flag system itself is a bit more advanced — it’s not just booleans you flip on and off; instead, you should support a wide range of functionality:</p><ul><li>User targeting (enabling the feature for certain users only, e.g., testers who do a final sanity check on production)</li><li>Geospatial targeting (enabling the feature in certain regions)</li><li>Percentage-based releasing (enabling the feature only to a certain amount of users)</li></ul><p>I would not recommend building a feature flag system, but evaluate if one of the existing SaaS platforms works for you — I would check out <a href="https://launchdarkly.com/">LaunchDarkly</a>.</p><p>In conclusion, deploying new functionality in development can be a complex process, especially as organizations grow and requirements evolve. By acknowledging the potential pitfalls of traditional deployment workflows and recognizing the unique challenges posed by production environments, we can proactively address issues and ensure a smoother deployment experience. Collaboration with other departments, such as customer service, operations, and marketing, becomes essential to maximize the impact of feature releases. Decoupling deployments from releasing through the use of feature flags provides a powerful solution, allowing for controlled feature availability and targeted releases. Leveraging existing SaaS platforms like LaunchDarkly can further streamline the implementation of feature flags. With a focus on collaboration, strategic planning, and the adoption of modern deployment practices, teams can deploy new functionality with confidence, empowering their organizations to deliver exceptional experiences to their customers.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=41e52169d3c6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/engineering-as-a-service/collaborative-confidence-empowering-teams-for-seamless-deployments-and-cross-departmental-success-41e52169d3c6">Collaborative Confidence: Empowering Teams for Seamless Deployments and Cross-Departmental Success</a> was originally published in <a href="https://medium.com/engineering-as-a-service">Engineering as a service</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to measure performance in Engineering]]></title>
            <link>https://medium.com/engineering-as-a-service/how-to-measure-performance-in-engineering-1a66bf8a666?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/1a66bf8a666</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[kpi]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[leadership]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Mon, 22 May 2023 00:00:51 GMT</pubDate>
            <atom:updated>2023-05-27T10:16:56.485Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JVOWjZCRkaeBMwgf" /></figure><p>In software engineering, measuring performance is crucial to understand how effectively teams deliver value and address customer challenges. To evaluate the effectiveness of engineering efforts, it’s important to establish meaningful metrics beyond completing workflow tasks. This article explores three essential metrics — speed, accuracy, and recovery — that provide valuable insights into engineering performance. By understanding and tracking these metrics, organizations can comprehensively view their engineering capabilities and drive continuous improvement.</p><h4>What is Engineering?</h4><p>First, we must agree on what Engineering is — a tool that takes customer challenges and produces and maintains solutions for those challenges. This tool usually prioritizes work through a roadmap that both Engineering and Product contribute too, but this is just how the tool works, not why it exists.</p><h4>Definition of done</h4><p>I’ve seen my fair share of Engineers who take on a ticket, write some code, push it, and then move the ticket to the next step in the workflow, thinking their work is finished. There is nothing inherently wrong with working like this, but it does have some downsides:</p><ul><li>The next step in the workflow (usually Quality Assurance) might bounce the task back.</li><li>The deployment did not go as expected.</li><li>The code might not work under a production load.</li><li>The product team might have expected something else and bounced the task back.</li><li>The product team might need to iterate over this functionality to resolve a customer challenge fully.</li></ul><p>A task should be considered as done when it’s in front of a customer and it solves the challenge it was intended to solve.</p><h4>The first metric I love to measure — speed</h4><p>Now that we have set the scene, the first metric becomes apparent — <strong>cycle time</strong>. Cycle time shows how long it takes for an Engineer to move a task in progress to the solution being in front of the customer and solving the customer’s pain point. This measurement usually encompasses the work of multiple people or teams of people:</p><ul><li>How long does it take to write the code — Engineers</li><li>How long does it take to test — Quality Assurance / Product</li><li>How long does it take to deploy — Infrastructure</li></ul><p>Knowing the cycle time allows you to drill into it, see where most of the time was spent, and optimize accordingly.</p><p>A small cycle time allows Engineering to produce value quickly and often — it shows the speed of Engineering.</p><h4>The second metric — accuracy</h4><p>Once you know how fast you move, you should focus on how accurately you move.</p><p>This metric, called the <strong>Change failure rate,</strong> shows how often changes that reach end-users require rollbacks, hotfixes, or other remedies. You can use this metric to measure how well set up your workflow of getting code from a developer’s laptop, through all quality checks, and in front of customers. A high change failure rate can mean many things:</p><ul><li>Engineers could use some guidance on the technical side of things</li><li>Quality assurance should take into consideration more aspects when testing things</li><li>The input given to Engineering from Product might need to be refined</li></ul><p>Knowing this metric shows you how accurately Engineering moves while drilling down helps you optimize your workflow. Having a low change failure rate helps with speed as well.</p><h4>The third metric — recovery</h4><p>Something will slip through the cracks regardless of how well your workflows are set up. You need to prepare for this, and we need to measure how long it takes for a problem to appear to it being solved. Problems can be an array of things — a new deployment not working, a service or database buckling under load, or that new fancy feature breaking something critical. For this, we use Mean Time to Resolution.</p><p>A high mean time for resolution can hint at problems in different areas:</p><ul><li>Your infrastructure might need changes, e.g., supporting blue/green deployment or faster rollbacks.</li><li>Perhaps your monitoring needs to be improved to catch these issues before they become critical — e.g., monitoring the load of services and databases.</li><li>Perhaps your observability (logs, traces, and metrics) needs changes to identify problems better.</li><li>Perhaps your deployment times need to be better aligned with people’s working hours</li><li>Maybe the path Engineers take for hotfixes could be sped up.</li></ul><p>A short resolution time shows how well Engineering is equipped to deal with unforeseen circumstances. It also has an added mental benefit — your team knows they can recover from issues quickly and will not cause major frustrations to your users.</p><h4>Summary</h4><p>These three metrics — speed, accuracy, and recovery — provide a solid starting point for assessing Engineering performance. While numerous other metrics are available to fine-tune processes, these initial measurements offer valuable insights into the effectiveness of Engineering practices. By focusing on speed, accuracy, and recovery, organizations can lay a foundation for continuous improvement, enabling Engineering teams to deliver optimal value to customers.</p><p><em>Originally published at </em><a href="https://fvst.dev/blog/measuring-engineering-performance"><em>https://fvst.dev</em></a><em> on May 22, 2023.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1a66bf8a666" width="1" height="1" alt=""><hr><p><a href="https://medium.com/engineering-as-a-service/how-to-measure-performance-in-engineering-1a66bf8a666">How to measure performance in Engineering</a> was originally published in <a href="https://medium.com/engineering-as-a-service">Engineering as a service</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unleashing the Power of Micro Frontends: Breaking Free from Monolithic Applications]]></title>
            <link>https://medium.com/engineering-as-a-service/unleashing-the-power-of-micro-frontends-breaking-free-from-monolithic-applications-78c47e59a145?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/78c47e59a145</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[microservices]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Fri, 19 May 2023 13:10:15 GMT</pubDate>
            <atom:updated>2023-05-25T08:55:33.945Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Arf1FeyDNlljKkciRlj_bw.png" /></figure><p>Embark on a transformation journey as we explore the challenges of a monolithic Single Page Application (SPA) and the solution: micro frontends. Discover how code splitting, server-side rendering, and modularization improve performance, scalability, and agility. From independent development and deployment to seamless scalability, this article unravels the benefits and potential downsides of adopting micro frontends. Join us as we revolutionize front-end architecture and embrace the future of web development.</p><h3>Humble beginnings</h3><p>We started many years ago with a single-page application, bundling code up with webpack and serving it to the customer. Once the application loads, navigating between pages is super fast as we only fetched the necessary data to render the new page.</p><p>So what <strong>problems</strong> does this approach have?</p><ul><li>All of the rendering is pushed to the client; if the client&#39;s phone is not the strongest, they will wait a while for our application to render on their phone.</li><li>All of the code is sent over the network to the customer for all of the pages in our system. Not problematic if you have 1 or 2 pages, but the more complex your system, the more the customer has to download. And downloading takes time.</li></ul><p>With our application&#39;s complexity growing with each new feature we pushed, we also increased the amount of javascript the client has to download, parse, and execute.</p><p>The problems started to grow when we were acquiring new customers in a new market — previously, we had people on iPhones using a 4g connection, and they loaded our site within seconds, while the market users had older generation Android phones using 3g and sometimes it took 60 seconds just to load our site. <strong><em>Change was needed</em></strong></p><h3>Server Side Rendering and code splitting to the rescue? We thought so too…</h3><p>We implemented code splitting, which significantly reduced the amount of Javascript we were shipping to the customer. We added server-side rendering so we could ship HTML to the customer, reducing the time the customer has to spend looking at an empty screen. Everything was faster and felt faster, so what’s the <strong>problem</strong>?</p><h4>It turns out that what we called a Single Page Application was actually a <strong>monolith</strong> and shared the same problems.</h4><ul><li>The application is too large and complex to fully understand and make changes fast and correctly.</li><li>The size of the application can slow down the build/start-up/deployment time.</li><li>You must redeploy the entire application on each update.</li><li>The impact of a change is usually not very well understood, which leads to do extensive testing.</li><li>Continuous deployment is difficult.</li><li>Monolithic applications can also be difficult to scale when different modules have conflicting resource requirements.</li><li>Another problem with monolithic applications is reliability. Bug in any module (e.g., memory leak) can potentially bring down the entire process. Moreover, since all instances of the application are identical, that bug will impact the availability of the entire application.</li><li>Monolithic applications have a barrier to adopting new technologies. Since changes in frameworks or languages will affect an entire application, it is extremely expensive in both time and cost.</li></ul><h3>What if we split up our front end into multiple smaller frontends? How would that work?</h3><p><strong>First</strong> off, we need something to distribute requests between these systems. We picked <a href="https://www.nginx.com/">Nginx</a> to handle this task, so let us start by implementing Nginx in front of our existing monolithic SPA.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*QNS4zYuJOQsN_CSy.png" /></figure><p><strong>Secondly</strong>, we start gathering user functionality in a single system. Let’s take the ID system first — it’s a system that handles signups, logins, and account recovery. We implement that functionality in our micro front end with server-side rendering and code splitting. We deploy our micro frontend separately and configure Nginx to route traffic with the prefix /id to our id micro frontend</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*h8-tIPoduNCLobOF.png" /></figure><p><strong>Thirdly</strong> we change our monolithic SPA routing so that all URLs starting with /id perform a full page navigation instead of our SPAs route event. Once we’re happy with the results, we can remove the id part of the code from our monolith.</p><h3>What benefits did we get from doing this?</h3><p>The ID part of your system is now a separate small system that you can:</p><ul><li><strong>develop</strong> independently without having to know the entire system. You could even have separate team work on this system.</li><li><strong>build</strong> quickly as the scope of the system has decreased immensely</li><li><strong>test</strong> easily as you have barriers between systems. Any changes you make to your id system can not break down the monolith and vice versa.</li><li><strong>deploy</strong> separately — no need to wait for other parts of the system to be production ready; deploy your micro frontend when you’re ready!</li><li><strong>scale</strong> based on traffic — some of your system pages will be accessed more than others; with micro frontends, you can configure part of your website to be scaled differently.</li><li><strong>rewrite</strong> to another language or framework without having to rewrite the entire system.</li></ul><p><strong>Turns out that the benefits of micro frontends are the same as microservices!</strong></p><h3>This can’t be all good now, can it?</h3><p>You’re right; this approach also has a few downsides.</p><ul><li>You must maintain the look and feel across different systems — a design system helps with this challenge!</li><li>Making changes across your entire front end is difficult — you must make changes to each part of the front end in isolation. Look into mono-repositories; turborepo is a really cool one! Have all your systems in one place and enjoy making changes that span across systems.</li><li>Having to make libraries to share code between micro frontends — your micro frontends will have a lot of repetition, and abstracting it away to a library is a need. Mono-repositories like <a href="https://turborepo.org/">turborepo</a> will help you with this problem as well!</li></ul><h3>Alternative solutions</h3><p>This solution was built about three years ago — since then, we’ve seen Module Federation gain popularity. This essentially means your application dynamically loads and executes Javascript on demand (similar to code splitting); the difference is that it dynamically loads a completely different application while sharing some of the dependencies (e.g., React or Angular).</p><p>Module federation is built around client-side rendering. It can support server-side rendering, but it’s not in a state where I would recommend using it in production just yet.</p><h3>Conclusion</h3><p>In conclusion, adopting micro frontends offers a transformative solution to the challenges posed by monolithic Single Page Applications (SPAs). Businesses can achieve enhanced performance, scalability, and agility by leveraging code splitting, server-side rendering, and modularization.</p><p>The article has explored the benefits of independent development and deployment, seamless scalability, and the ability to rewrite parts of the system without overhauling the entire application. Micro frontends align with the advantages of microservices, enabling teams to work independently, develop rapidly, test easily, and deploy separately.</p><p>While the benefits are substantial, it is important to acknowledge the potential downsides of micro frontends. Maintaining consistent user experience across different systems can be challenging, but utilizing a design system can alleviate this concern. Making changes that span across the entire front end can be difficult, requiring isolated modifications to each part. Solutions such as mono-repositories, including the innovative turborepo, can facilitate coordinated changes across systems and mitigate repetition.</p><p>Overall, embracing micro frontends empowers organizations to revolutionize their front-end architecture, paving the way for the future of web development. By breaking free from monolithic applications and embracing modularization, businesses can unlock the power of micro frontends and unleash the full potential of their digital experiences</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=78c47e59a145" width="1" height="1" alt=""><hr><p><a href="https://medium.com/engineering-as-a-service/unleashing-the-power-of-micro-frontends-breaking-free-from-monolithic-applications-78c47e59a145">Unleashing the Power of Micro Frontends: Breaking Free from Monolithic Applications</a> was originally published in <a href="https://medium.com/engineering-as-a-service">Engineering as a service</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unlocking the Power of Listening: Navigating the Three Levels of Active Listening]]></title>
            <link>https://medium.com/engineering-as-a-service/unlocking-the-power-of-listening-navigating-the-three-levels-of-active-listening-2c2a109ef0c8?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/2c2a109ef0c8</guid>
            <category><![CDATA[meetings]]></category>
            <category><![CDATA[leadership]]></category>
            <category><![CDATA[coaching]]></category>
            <category><![CDATA[mentorship]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Mon, 15 May 2023 14:54:36 GMT</pubDate>
            <atom:updated>2023-05-25T08:57:25.986Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LREW_BpU6tWS87M2yKmhZg.png" /></figure><p>Listening is one of the most complex skills for a leader, as we are intrinsically trained to be solution providers. We like to look after people and often take ownership of issues. Sometimes we must sit quietly and ensure the other person is heard.</p><p>It is one thing to listen to a person attentively. However, the art of hearing someone, understanding what they are saying, what they mean, and responding to that is an advanced art form.</p><p>There are three levels of listening. When used at the correct time and in the right way, each level allows us to get the most out of our conversations.</p><h3><strong>Level 1: Internal Listening (Focused on Self)</strong></h3><p>At this level, you focus on yourself and your reactions to the other person&#39;s words.</p><p>You could be thinking about how you&#39;ve had a similar experience to what this person is talking about or that you disagree with what they are saying.</p><p>You are reacting to what they say, which is excellent in a mentorship situation but not helpful in a coaching environment.</p><p>You might miss crucial information and rush into sharing your thoughts, leaving the person unheard.</p><h3>Level 2: Focused Listening (Focused on Other)</h3><p>You are unattached from yourself, your thoughts, agenda, and opinions at this level. You are instead focused on the person, what they are saying, and taking all of that in.</p><p>This might seem natural in certain situations, e.g., listening to someone significant to us, but in other scenarios, it may be something we need to make a conscious effort to do. To practice level 2 listening, take a few deep breaths before conversing. Let go of anything that happened prior, or that will happen after.</p><p>At this level, it&#39;s normal to fall back to level one — e.g., you might start thinking about what is an excellent question to ask next. When you catch yourself in this situation, take a breath and refocus on the conversation.</p><h3>Level 3: Global Listening (Focused on context and what isn&#39;t being said)</h3><p>At this level, you are tuned to the person. You not only listen to the words but the tone, the pauses in conversation, and their body language.</p><p>To reach this level, you must learn to pick up on the non-verbal parts of communication. Things like</p><ol><li>Facial expressions</li><li>Gestures</li><li>Paralinguistics (such as loudness or tone of voice)</li><li>Body language</li><li>Proxemics or personal space</li><li>Eye gaze, haptics (touch)</li><li>Appearance</li></ol><p>Once you master this level, you can go to any conversation; the other part will feel like you are truly listening to them!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2c2a109ef0c8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/engineering-as-a-service/unlocking-the-power-of-listening-navigating-the-three-levels-of-active-listening-2c2a109ef0c8">Unlocking the Power of Listening: Navigating the Three Levels of Active Listening</a> was originally published in <a href="https://medium.com/engineering-as-a-service">Engineering as a service</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Revamp Your Retrospectives: Unlocking the Power of Productive Team Reflections]]></title>
            <link>https://medium.com/engineering-as-a-service/revamp-your-retrospectives-unlocking-the-power-of-productive-team-reflections-f3820c346cff?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/f3820c346cff</guid>
            <category><![CDATA[meetings]]></category>
            <category><![CDATA[retrospectives]]></category>
            <category><![CDATA[coaching]]></category>
            <category><![CDATA[team-building]]></category>
            <category><![CDATA[leadership]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Mon, 15 May 2023 06:57:35 GMT</pubDate>
            <atom:updated>2023-05-25T08:58:44.105Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w3U130zwHBsn9K9huhOCrQ.png" /></figure><p>Retrospectives are crucial when building any team. They allow you to reflect on your time together as a team and learn from it. But they often go over the time limit and turn into arguing; to stop this from happening, we recommend a different approach than the regular “Start doing, stop doing, continue doing.”.</p><h3>Preparation needed for any successful retrospective</h3><p>You must set the scene — tell your team that you have scheduled a retrospective and expect them to take 30 minutes before the meeting to put their thoughts on paper. Ask them to think about each team member individually, the whole team, and the processes you have inside the team. Encourage people to highlight positive experiences and remind them not to blame others; instead, share how their actions made them feel.</p><h3>The format of the meeting</h3><p>We prefer to split retrospectives into four different parts.</p><p><strong>Housekeeping</strong></p><p>At the start of the meeting, explain to everyone how the meeting will go. Give everyone a piece of paper and a pen to take notes.</p><p><strong>Listening to each other</strong></p><p>This part focuses on listening to each other. You go through each team member and ask them to share their insights — they are the only ones speaking, and everyone else listens.</p><p>The person speaking shares what they loved about working with each team member and what they could do differently. They talk about how they saw the team and the processes, what worked for them, and what did not.</p><p>The people not talking can use this time to make notes about things they would like to discuss.</p><p><strong>Discussing what they heard</strong></p><p>Once everyone has had a chance to speak, it’s time to discuss. People can review their notes and respond to what they heard — to clarify, understand, and share their views.</p><p>This part of the meeting will be the loudest. Some people are more vocal and want to discuss more in-depth, but it’s your job as the leader to make sure the less vocal ones are also heard. Divert the conversation away from the more vocal ones by asking the less vocal ones questions like “What are your thoughts on this?”</p><p>You do not have to go around each person and ask their thoughts on the subject, but you have to create space in the conversation for the less vocal ones to chip in.</p><p><em>P.S.</em> Be mindful of time, don’t let this part drag on too long. If a topic takes away too much of the time, state that it won’t be resolved in this discussion and ask if someone wants to lead fixing this outside the meeting.</p><p><strong>Sanity check</strong></p><p>In the final round, ask each individual if they have anything they want to add or discuss. This makes sure everyone has had a chance to speak up. Once a team feels more comfortable with retros and the format, you will probably start getting “No” answers, but it’s still good practice to ask this question.</p><h3>That’s it!</h3><p>This retrospective format works for teams in different stages of development. This format works for you even if you just got together or have been working together for three years. Try it out, and let me know how it goes!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f3820c346cff" width="1" height="1" alt=""><hr><p><a href="https://medium.com/engineering-as-a-service/revamp-your-retrospectives-unlocking-the-power-of-productive-team-reflections-f3820c346cff">Revamp Your Retrospectives: Unlocking the Power of Productive Team Reflections</a> was originally published in <a href="https://medium.com/engineering-as-a-service">Engineering as a service</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unlocking the Need for Speed: The Surprising Solution That Supercharges Engineering Departments]]></title>
            <link>https://medium.com/engineering-as-a-service/want-your-engineering-to-achieve-more-15010c45a45c?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/15010c45a45c</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Sat, 13 May 2023 08:27:09 GMT</pubDate>
            <atom:updated>2023-05-25T09:00:09.540Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CyPF9TOHoAEXMZLViYHVIA.png" /></figure><p>We’ve been spending our time helping all the different size Engineering departments, and one standard and easy improvement keeps popping up — CI speed. This article will explore the significance of CI speed and ways to enhance it in any programming language. One of the primary advantages of boosting CI speed is its ability to combat context switching, which can adversely impact productivity. By having a faster CI process, we can cultivate a work environment that views errors as learning opportunities, enables quick recovery from mistakes, and accelerates deployment to production. We will discuss various techniques to improve CI speed, such as caching and utilizing smaller docker images, and offer practical examples of implementing these techniques in Node and other programming languages.</p><h3>Why does CI speed matter?</h3><p>The main benefit is to battle context switching. When a developer opens a pull request and then needs to wait 10 minutes for feedback, they switch their focus to something else. It reminds me of an old xkcd comic</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/413/1*J-1MC3QGbIuwq4tb-yr-iA.png" /><figcaption><a href="https://xkcd.com/303/">https://xkcd.com/303/</a></figcaption></figure><p>But you can always switch to another task! And that’s the problem; you switch the context — it kills productivity.</p><ul><li><a href="https://hbr.org/2010/05/how-and-why-to-stop-multitaski">https://hbr.org/2010/05/how-and-why-to-stop-multitaski</a></li><li><a href="https://www.atlassian.com/blog/productivity/context-switching">https://www.atlassian.com/blog/productivity/context-switching</a></li></ul><p>When you finally come back to the task at hand, at best, all you have to do is merge the PR, but at worse, you have to go back to the branch and make code changes! This requires you to switch your context back to this task.</p><h3>All my developers are superhuman, and they can handle context-switching!</h3><p>If that’s the case, there are still benefits to speeding up CI.</p><p>Speed is crucial for building an environment that looks at mistakes as learning experiences. You should have a way to recover from any error quickly — if someone pushes something into production that should not be there, they should know they can fix their mistake in 3 minutes instead of 30. Rollbacks are not always an option.</p><p>Speed is crucial for pushing more stuff to production. CI is more than just running tests; it’s a pipeline for getting your code off your developer’s machine to a test environment and production. The faster this pipeline is, the more effective the people that rely on this pipeline are.</p><p>Speed has monetary value. For example, Github action round-up times to a minute; if your jobs run for 1.01 minutes, you get billed for 2 minutes. Reducing the time by 2 seconds saves you 50% on your CI bill.</p><h3>So how can we make CI faster?</h3><p>In this article, I’ll explain the steps you can take to improve speed using Node and the tooling around Node as examples. I’ll review why these examples make things faster and how to apply them to your language and toolset.</p><p>Package managers and global caches.</p><p>Most projects on Github Actions use <a href="https://github.com/actions/setup-node#caching-global-packages-data">actions/setup-node</a>. This action has caching for the global cache built into it, meaning it does not cache node_modules but instead caches the package manager’s global cache. This means whenever your job runs, all your packages are cached in the global cache, and running npm/yarn install copies your packages from the global cache to node_modules.</p><p>There are a couple of problems with this approach.</p><ul><li>Yarn 1 only uses this cache if the internet is not available. <a href="https://github.com/yarnpkg/yarn/issues/6398">https://github.com/yarnpkg/yarn/issues/6398</a></li><li>You are doubling the IO operations that are required. First, you write the entire cache to the global cache, and then you copy from the global cache to your local dependencies. Disk is slow when it comes to copying thousands of small files. Circle CI tackles this issue by using a <a href="https://support.circleci.com/hc/en-us/articles/360054908812-Speed-up-steps-using-a-RAM-disk">ramdisk</a>, essentially keeping the files in memory instead of writing to disk.</li></ul><p>The easiest solution is not to use the global cache and simply cache node_modules using <a href="https://github.com/actions/cache#cache-action">actions/cache</a>. This approach does have some disadvantages:</p><ul><li>Hooks like post-install are only run once when you install the package for the first time. This can become problematic if you are using packages that write stuff outside of the node_modules folder or do some work based on the state of the code in your code base, which should be run each time a job is started.</li><li>The cache can become invalid if not purged after updating the node version. This can easily be solved by adding the node version to the cache key.</li></ul><h3>Tooling and caches</h3><p>A lot of tooling has built-in support for caching. In the node world, the community agreed upon cache folder is node_modules/.cache/, which some libraries already use, and others can be configured to use.</p><ul><li>(Transpiling) Typescript supports incremental builds and caching — <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#faster-subsequent-builds-with-the---incremental-flag">https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#faster-subsequent-builds-with-the---incremental-flag</a></li><li>(Static analysis) Eslint supports caching — <a href="https://eslint.org/docs/latest/use/command-line-interface#caching">https://eslint.org/docs/latest/use/command-line-interface#caching</a></li><li>(Test runner) Jest supports caching — <a href="https://jestjs.io/docs/configuration#cachedirectory-string">https://jestjs.io/docs/configuration#cachedirectory-string</a></li><li>(Code formatting) Prettier already uses this folder out of the box. <a href="https://prettier.io/docs/en/cli.html#--cache">https://prettier.io/docs/en/cli.html#--cache</a></li></ul><p>P.S. Make sure your toolchain is optimized for the CI environment. For example, many setups use the <a href="https://jestjs.io/docs/cli#--maxworkersnumstring">max-workers=50%</a> option to run Jest. Since most Github action workers have 2 CPU cores, you’re artificially limiting the test suite to run on a single core, reducing your speed significantly.</p><h3>If you use docker, ensure your images are as small as possible.</h3><p>Having smaller docker images speeds up your infrastructure quite a bit.</p><ul><li>Smaller images are faster to upload to a registry, speeding up the CI flow.</li><li>Smaller images are faster to download from a registry, speeding up redeploy/scale operations. This is highly noticeable if you’re doing multi-region deploys.</li><li>Smaller images cost less, both from a storage point of view and a bandwidth point of view.</li></ul><p>The first step to making your docker images smaller is to use an appropriate base image. I prefer <a href="https://www.docker.com/blog/how-to-use-the-alpine-docker-official-image/">alpine images</a>, and most languages have base images based on alpine.</p><p>The second step is to use <a href="https://www.docker.com/blog/advanced-dockerfiles-faster-builds-and-smaller-images-using-buildkit-and-multistage-builds/">multi-stage builds</a> to separate the build process from the final image. The flow I usually use is:</p><ul><li>In the build stage, I install all dependencies and run the build. I then purge all devDependencies to reduce the size of the node_modules folder — these dependencies should not be required to run the application.</li><li>In a cleanup stage, I copy over node_modules from the build stage and purge extra files like markdown files, test suites, etc. A helpful library for this is <a href="https://github.com/tj/node-prune">node-prune</a>. Since this is a GO project, the cleanup stage usually uses a GO base image.</li><li>In the final stage, I copy over the built application from the build stage and the node_modules from the cleanup stage.</li></ul><p>To validate that I don’t have any wasted space, I love the utility <a href="https://github.com/wagoodman/dive">https://github.com/wagoodman/dive</a>, which shows me how much space each layer takes and what it adds.</p><p>P.S. Make sure you use a <a href="https://shisho.dev/blog/posts/how-to-use-dockerignore/">dockerignore</a> file; it helps with speed, size, and security!</p><h3>Take a look at your workflows.</h3><p>To save time, a lot of CI workflows are run in parallel — one job to run the test suite, one job to run static analysis, and one test suite to run the linting. After adding caching, a common occurrence is that checking out and setting up the environment on the task runner takes longer than the actual work. So instead of having three jobs in parallel, having a single job that does it all is usually more effective. If a single job takes less than a minute instead of 3 jobs taking less than a minute, you’ve effectively cut your CI bill by 66%.</p><p>Think through if you need to run all tasks on a main branch. If you enforce all branches to be up to date with the main branch, running the test suite on the main branch doesn’t add anything. If you want static analysis on the main branch, move that job to a scheduled job instead of running on each commit to the main branch.</p><h3>Final words</h3><p>With all these steps, you can significantly improve the performance of your Engineering department. Make sure you’re utilizing all the resources of the task runner, reduce the amount of disk writes you do, use ramdisk if possible for IO, and make sure your docker containers are small!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=15010c45a45c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/engineering-as-a-service/want-your-engineering-to-achieve-more-15010c45a45c">Unlocking the Need for Speed: The Surprising Solution That Supercharges Engineering Departments</a> was originally published in <a href="https://medium.com/engineering-as-a-service">Engineering as a service</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Striking the Balance: The Pros and Cons of Standardization in Software Engineering Teams]]></title>
            <link>https://medium.com/engineering-as-a-service/are-standards-the-best-path-in-engineering-2098ec47bec1?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/2098ec47bec1</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[cto]]></category>
            <category><![CDATA[engineering-mangement]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Sat, 13 May 2023 06:06:43 GMT</pubDate>
            <atom:updated>2023-05-25T09:01:17.749Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ybXHcukfk3v9fFqdrfjHFA.png" /></figure><p>Questions around standardization often arise when an Engineering team grows. What kind of code style rules should we follow? What type of language should we use? What tools, frameworks, and libraries do we use?</p><p>This article delves into standardization in software engineering teams and highlights its pros and cons. While standardization can bring advantages like consistency, quality, and efficiency, it can also have negative impacts like limiting creativity, causing inefficiencies, and decreasing motivation. Additionally, standardization can limit flexibility, increase the risk of errors, and affect developer retention.</p><h3>What are the benefits of standardization?</h3><p><strong>Consistency</strong></p><p>All your engineers build things the same way! This eliminates potential bottlenecks; someone else can take over with minimal overhead if someone falls sick. Going from project to project is easy!</p><p><strong>Quality</strong></p><p>Once you have chosen the best tools for the job and learned the best ways to use them, you see a boost in quality. You can easily avoid making the same mistakes in a new project using the knowledge you’ve gained from previous projects.</p><p><strong>Efficiency</strong></p><p>This is a combination of the two previous points — when how people work is agreed upon, the focus becomes on what is being worked on. Instead of worrying about what library is being used, you can focus more on the project’s business requirements. And with better quality, you spend less time on bugs!</p><p><strong>Onboarding new people</strong></p><p>If your standards are correctly documented, it is much easier for new people to join the team; they become effective team members much sooner.</p><h3>This sounds amazing! But is there a catch?</h3><p>Standardization also has a few drawbacks, less evident than the benefits!</p><p><strong>Limited creativity</strong></p><p>You might lose innovation if you have established rules for building things! An Engineer might have unique ideas and approaches that bring value to the department, but they get dismissed or overlooked in favor of standardization.</p><p><strong>Inefficiency</strong></p><p>Different projects have different requirements — and if they are all handled by the same set of rules, you create inefficiencies. For example, if you’ve agreed to use node as a language in your company but have a new service that does CPU-intensive work, then the agreed-upon tool will produce a subpar result.</p><p><strong>Decreased motivation</strong></p><p>Engineers may become less motivated or engaged in their work if they cannot make their own decisions or use their preferred tools and processes. This can lead to decreased productivity and quality of work.</p><p><strong>Limited flexibility</strong></p><p>Change is inevitable. Your company might move in a completely new direction requiring a standards change. Instead of being able to deliver value, you have to spend time adapting to standards that do not fit your project or take on the extra work of changing them. Changing standards is extremely hard as they require you to change the minds of everyone involved. Standards also affect everything your team has built thus far, so a change might cause issues with the work that has already been finished.</p><p><strong>Higher risk of errors</strong></p><p>Although standards reduce the risk of various bugs, a bug in a standard process or tool propagates to every project you’ve built!</p><p><strong>It affects developer retention.</strong></p><p>We’ve all grown tired of something and want a change at one point. If all your teams work the same way, motivating a team member to try a different team instead of trying another job is hard.</p><h3>So what should I do?</h3><p>I recommend having different standards levels; some are department-level standards that all teams must follow, while some are team-level standards that only the team should follow.</p><p>Department-level standards should be more universal, e.g., where you host and deploy your code, while team-level standards should be more specific, e.g., what framework you use.</p><p>This allows you to have most of the benefits standardization brings while also addressing the downsides!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2098ec47bec1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/engineering-as-a-service/are-standards-the-best-path-in-engineering-2098ec47bec1">Striking the Balance: The Pros and Cons of Standardization in Software Engineering Teams</a> was originally published in <a href="https://medium.com/engineering-as-a-service">Engineering as a service</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I would architect a new online betting platform in 2022]]></title>
            <link>https://medium.com/@peeter.tomberg/how-i-would-architect-a-new-online-betting-platform-in-2022-4f8f212ca4bd?source=rss-b2f05c1a608a------2</link>
            <guid isPermaLink="false">https://medium.com/p/4f8f212ca4bd</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[betting]]></category>
            <category><![CDATA[engineering]]></category>
            <dc:creator><![CDATA[Peeter Tomberg]]></dc:creator>
            <pubDate>Sun, 27 Feb 2022 22:55:46 GMT</pubDate>
            <atom:updated>2022-02-27T22:55:46.240Z</atom:updated>
            <content:encoded><![CDATA[<p>What technologies would I pick and why?</p><p>Let us get started with a diagram! This one was built using the <a href="https://c4model.com/">c4 model</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GM8gHkERROZEWdZjSckMMg.png" /><figcaption>Overview of technologies</figcaption></figure><h4>So what are the main things we’re trying to achieve with this architecture?</h4><ol><li>Speed</li><li>Reliability</li><li>Scalability</li></ol><h3>Let’s get started with edge networks</h3><p>One of the best ways to achieve speed is to reduce the distance between your servers and your customers. If your servers are in Germany and your customer is in Canada, the time it takes for data to physically transfer between such a long distance is very significant. You could alleviate this by having servers in Canada in a data center as well, but the distance between a customer and a data center is still a lot. In the old times, we used to use CDNs to cache static files close to a customer, a CDN network is a group of servers scattered around the world with the intention of being close to the customer. You fetch your files from the CDN which in return fetches and caches them from your servers. Edge networks take the same approach to a new level — allowing you to run your code on the same server as your CDN. This allows you to be as close as possible to your customer and we can offload a lot of logic to the edge:</p><ol><li>SSL termination</li><li>DNS and routing</li><li>Compression</li><li>Caching</li><li>Authentication</li></ol><p>I personally enjoy <a href="https://vercel.com/">Vercel</a> infrastructure, but you can use the same with <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> for example. In our graph, a user makes a request against our site, the request goes to the edge network which resolves DNS to your datacenter, terminates SSL and handles authentication, and then routes the request to our frontend. When the request comes back, it compresses it and caches when applicable.</p><h3>The request is sent to our frontend</h3><p>Our request is handled by the Next JS application running on Vercel network. It’s responsible for server-side rendering, in which it makes requests against the in-built API of the app running on serverless functions. These serverless functions can be in a multitude of programming languages and return data in any format you prefer — JSON, REST, gRPC-web, graphQL. These serverless functions are responsible for communicating with your backend over gRPC / HTTP2. Think of these as your backend-for-frontend, if your frontend application requires any sort of data transformation it can be done here instead of coupling the backend together with the frontend.</p><h4>What about real-time data updates?</h4><p>Once your Next JS application has fetched all its data needs and sent HTML back to the client, the browser takes over and creates an EventSource against <a href="https://mercure.rocks/">Mercure Hub</a> — essentially letting Mercure Hub know that it is interested in this topic and would like the server to push events to it whenever something changes. This pattern allows our APIs to be stateless while still having real-time communications. Whenever our backend has an update on a specific topic, we make a POST call to Mercure Hub and it then relays the information to all interested clients. We prefer Server-Sent Events over WebSockets as they work over HTTP2 and we really don’t need bidirectional communication, just the server pushing updates to us.</p><h3>Let’s move on to the backend</h3><p>For the backend, we love gRPC as it allows us to define an interface to our API that we then implement. The technology you use to implement the API is up to you, if the service is IO heavy then consider using Node JS, if your service requires multiple cores you can use the event bus to distribute workload between multiple instances of your Node JS service. If your data loads are big and you need to process them quickly, consider something that takes advantage of multiple cores and can share memory. Using gRPC enables you to swap out the underlying technology while keeping the same interface, so you can rewrite with ease when a need for it arises. As an added benefit, since gRPC is binary and uses HTTP2, your infrastructure network load will be heavily reduced.</p><h4>What do you think? Did we miss anything? Would you prefer another technology? Let us know!</h4><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4f8f212ca4bd" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>