{"name":"languages","slug":"languages","paginate_by":null,"paginate_path":null,"render":true,"feed":false} {"name":"Java","slug":"java","path":"/languages/java/","permalink":"https://martin-thurau.de/languages/java/","pages":[{"relative_path":"story/drklein.md","colocated_path":null,"content":"<p>Dr. Klein Privatkunden is a financial intermediary for property loans, insurance and instalment loans for consumers. It\nuses <a href=\"https://www.holacracy.org/\">Holocracy</a> as it's organization scheme, which provides a unique way of influencing the\ndecision making process of the organization. I worked in the Team &quot;Plattform&quot; to create a software platform that connects\nproperty-related partners like real estate agents and architects with customers. <span id=\"continue-reading\"></span> When I joined, the\nproject was in it's beginning stages so I had the opportunity to work on various parts of the project:</p>\n<h2 id=\"development-lead-for-the-brand-new-customer-portal-from-start-to-production\">Development lead for the brand new customer portal from start to production</h2>\n<p>The customer portal is based on a micro-frontend architecture written in Angular that communicates with multiple Spring\nBoot microservices in the backend via a RESTful API, which are all tied together by a Kafka based event system. It\nfeatures a section for the personal data of customers, a view on the current customer service agent, a multi-step wizard\nfor all the relevant property loan data and a document upload. Property loans require a lot of paperwork which was\npreviously managed manually and offline by sales agents. The document upload provides a way for customers to upload all\nthe relevant documents which are then automatically organized according to their type and are directly available in the\nsoftware that the sales agents use. This greatly reduced the complexity of this part of the customer journey by making\nthe process fully digital for the customer and our sales agents.</p>\n<h2 id=\"maintenance-for-login-and-registration\">Maintenance for login and registration</h2>\n<p>With the growing number of features and team members I took over the maintenance and future development of our core\nauthentication and authorization system and the API gateway. All users of the system authenticate via an OAuth2 based\nlogin system and all internal and external requests are validated by a central API gateway before being processed. This\nallowed us to publicly expose all of our APIs which makes integration of other internal and external systems easy. We\nalso dogfooded our APIs with our own frontends. My tasks involved continued improvement and bug-fixing of the login and\nregistration process and the aforementioned authentication process.</p>\n<h2 id=\"involvement-and-architecture-und-devops\">Involvement and Architecture und DevOps</h2>\n<p>I was part of the architecture and devops circles. The architecture circle aimed to facilitate the continued improvement\nof our general architecture and also served as a decision making entity for it. It is also the place to exchange, discuss\nand approve architectural decisions of the individual subteams which we do with regular work meetings. The DevOps circle\nbrought together all developers that hold the &quot;DevOps hat&quot; in the subteams. We used an infrastructure-as-code based\napproach based on Terraform (for general provisioning) and Kubernetes. Most of the actual day-to-day devops work was\nmostly handled autonomously by the developers in the subteams. This circle was the central place to discuss ideas and\nsolve problems. We also did most of the overarching groundwork here.</p>\n<h2 id=\"migrating-our-event-system-from-rabbitmq-to-kafka\">Migrating our event system from RabbitMQ to Kafka</h2>\n<p>Our microservice architecture used an event based architecture for propagating state and data between services and\nespecially context boundaries. Those events were previously published via RabbitMQ in publish/subscribe configuration.\nIn order to keep logical consistency we wanted to keep certain ordering guarantees which required custom logic for all\nreaders. This proved to be a cumbersome and poorly performing part of our overall architecture so we decided to switch\nover to Kafka as an event bus. My work here involved prototyping and testing, writing and maintaining the shared\nintegration library (based on Spring Kafka) as well as supporting other developers in the migration.</p>\n<h2 id=\"maintaining-internal-libraries\">Maintaining internal libraries</h2>\n<p>Additionally to the Kafka related library I also maintained an internal Java library for our Spring Boot microservices.\nThe library handles common tasks for monitoring, tracing, authentication for internal and external requests and testing.</p>\n<h2 id=\"exploring-new-technologies-and-tools\">Exploring new technologies and tools</h2>\n<p>In some extra time I explored various new tools and languages and introduced them to the team. I brought in Kotlin as a\nsecondary JVM language for our Spring Boot microservices (the document upload is fully written in Kotlin) and also\ndeveloped an auxiliary monitoring tool with Rust to get some insight into the language. Additionally, I wrote a Yeoman\nbased code generator to reduce the amount of code that has to be written for new entities which saved a lot of typing\nhours for my teammates and me.</p>\n<h2 id=\"developer-support\">Developer support</h2>\n<p>Being a full stack developer and also a pretty early member of the team I knew most parts of the system to some degree.\nBecause of this I was frequently asked by other team members about details of our system. I was also sometimes involved in\ndebugging and the onboarding of new team members.</p>\n<p>Additionally I also held multiple Holocracy related roles such as facilitator (who leads and mediates meetings) and\nsecretary (who organizes and documents said meetings).</p>\n","permalink":"https://martin-thurau.de/story/drklein/","slug":"drklein","ancestors":["_index.md","story/_index.md"],"title":"Dr. Klein Privatkunden AG","description":null,"updated":null,"date":null,"year":null,"month":null,"day":null,"taxonomies":{"languages":["Kotlin","Java","Rust","Javascript","Typescript"],"technologies":["Spring Boot","Kafka","MySQL"]},"authors":[],"extra":{"work_from":"2019-04-01","work_until":"2021-09-30","link":"https://www.drklein.de","logo":"story/drklein_logo.png"},"path":"/story/drklein/","components":["story","drklein"],"summary":"<p>Dr. Klein Privatkunden is a financial intermediary for property loans, insurance and instalment loans for consumers. It\nuses <a href=\"https://www.holacracy.org/\">Holocracy</a> as it's organization scheme, which provides a unique way of influencing the\ndecision making process of the organization. I worked in the Team &quot;Plattform&quot; to create a software platform that connects\nproperty-related partners like real estate agents and architects with customers. ","toc":[{"level":2,"id":"development-lead-for-the-brand-new-customer-portal-from-start-to-production","permalink":"https://martin-thurau.de/story/drklein/#development-lead-for-the-brand-new-customer-portal-from-start-to-production","title":"Development lead for the brand new customer portal from start to production","children":[]},{"level":2,"id":"maintenance-for-login-and-registration","permalink":"https://martin-thurau.de/story/drklein/#maintenance-for-login-and-registration","title":"Maintenance for login and registration","children":[]},{"level":2,"id":"involvement-and-architecture-und-devops","permalink":"https://martin-thurau.de/story/drklein/#involvement-and-architecture-und-devops","title":"Involvement and Architecture und DevOps","children":[]},{"level":2,"id":"migrating-our-event-system-from-rabbitmq-to-kafka","permalink":"https://martin-thurau.de/story/drklein/#migrating-our-event-system-from-rabbitmq-to-kafka","title":"Migrating our event system from RabbitMQ to Kafka","children":[]},{"level":2,"id":"maintaining-internal-libraries","permalink":"https://martin-thurau.de/story/drklein/#maintaining-internal-libraries","title":"Maintaining internal libraries","children":[]},{"level":2,"id":"exploring-new-technologies-and-tools","permalink":"https://martin-thurau.de/story/drklein/#exploring-new-technologies-and-tools","title":"Exploring new technologies and tools","children":[]},{"level":2,"id":"developer-support","permalink":"https://martin-thurau.de/story/drklein/#developer-support","title":"Developer support","children":[]}],"word_count":809,"reading_time":5,"assets":[],"draft":false,"lang":"en","lower":null,"higher":null,"translations":[{"lang":"de","permalink":"https://martin-thurau.de/de/story/drklein/","title":"Dr. Klein Privatkunden AG","path":"/opt/buildhome/repo/content/story/drklein.de.md"},{"lang":"en","permalink":"https://martin-thurau.de/story/drklein/","title":"Dr. Klein Privatkunden AG","path":"/opt/buildhome/repo/content/story/drklein.md"}],"backlinks":[]},{"relative_path":"story/esailors.md","colocated_path":null,"content":"<p>eSailors is a software provider for online lottery platforms. They run a mutli-tenant, multi-language eCommerce platform\nwith over 400.000 monthly active users. I worked in the &quot;Games&quot; area which provides the front- and backend parts for the\nactual lottery experience of the platform.<span id=\"continue-reading\"></span></p>\n<h2 id=\"development-and-improvement-of-lotteries\">Development and improvement of lotteries</h2>\n<p>The stack is mainly JVM focused with over 70 micro (sometimes not-so-micro) services all running inside a Kubernetes\ncluster. I worked in the front- and backend to add new features to existing lotteries as well as implement completely\nnew ones. Implementing new lotteries involved touching many parts of our system from the actual UI, over the purchase\nprocess into the ticket history view as well as payment and hedging. Some of the work was done directly by team “Games”\nwhile others where coordinated with other teams inside eSailors.</p>\n<h2 id=\"extracting-the-lotteries-into-micro-frontends\">Extracting the lotteries into micro-frontends</h2>\n<p>The core frontend (called “the webshop”) was a Spring-Boot + Angular monolith that took a long time to deploy due to an\nover-complicated build system and hundreds of (sometimes flaky) integration tests. To gain the ability to deploy our\nassets independently the team Games started extracting our games core into a separate project that could be tested and\ndeployed independently and was then loaded into the core frontend via a small wrapper using an IFrame. While this worked\nout great eventually there were lots of challenges on the way.</p>\n<h2 id=\"migration-from-angularjs-to-angular\">Migration from AngularJS to Angular</h2>\n<p>The frontend for the lotteries was originally done with AngularJS. During my time we completely migrated the lotteries\nfrontend themself and also most parts of the rest of the frontend from AngularJS to Angular. This was done iteratively\nwhile keeping the shop running and continuously deploying to production. The frontend was a large, monolithic\napplication that also involved some server side rendering for certain pages. Keeping that all together and running\nsmoothly during such an invasive migration uncovered lots of interesting corner cases and bugs that we had to solve to\nkeep the site running</p>\n<h2 id=\"internationalization-of-lottery-platform\">Internationalization of lottery platform</h2>\n<p>The company decided to go into the international lottery market (starting with Ireland, followed by Scotland and\nRomania). The system was previously German only and not, in the most parts, no preparation for internationalization.\nAdding this to the front- and backend was a huge task that occupied most of the engineers for a long time. We added\nsupport for multiple languages but also different currencies and had a lot of fun fixing layout bugs because text was\nnow suddenly a lot shorter or longer than previously anticipated and finding a solution where we programmatically built\ntext from snippets.</p>\n","permalink":"https://martin-thurau.de/story/esailors/","slug":"esailors","ancestors":["_index.md","story/_index.md"],"title":"eSailors IT Solutions GmbH","description":null,"updated":null,"date":null,"year":null,"month":null,"day":null,"taxonomies":{"languages":["Typescript","Java","Javascript"],"technologies":["Angular","AngularJS","Kubernetes","Protractor","Jasmine","Pupeteer"]},"authors":[],"extra":{"work_from":"2016-10-01","work_until":"2019-03-31","logo":"story/esailors_logo.png","link":"https://www.lotto24.de/"},"path":"/story/esailors/","components":["story","esailors"],"summary":"<p>eSailors is a software provider for online lottery platforms. They run a mutli-tenant, multi-language eCommerce platform\nwith over 400.000 monthly active users. I worked in the &quot;Games&quot; area which provides the front- and backend parts for the\nactual lottery experience of the platform.","toc":[{"level":2,"id":"development-and-improvement-of-lotteries","permalink":"https://martin-thurau.de/story/esailors/#development-and-improvement-of-lotteries","title":"Development and improvement of lotteries","children":[]},{"level":2,"id":"extracting-the-lotteries-into-micro-frontends","permalink":"https://martin-thurau.de/story/esailors/#extracting-the-lotteries-into-micro-frontends","title":"Extracting the lotteries into micro-frontends","children":[]},{"level":2,"id":"migration-from-angularjs-to-angular","permalink":"https://martin-thurau.de/story/esailors/#migration-from-angularjs-to-angular","title":"Migration from AngularJS to Angular","children":[]},{"level":2,"id":"internationalization-of-lottery-platform","permalink":"https://martin-thurau.de/story/esailors/#internationalization-of-lottery-platform","title":"Internationalization of lottery platform","children":[]}],"word_count":439,"reading_time":3,"assets":[],"draft":false,"lang":"en","lower":null,"higher":null,"translations":[{"lang":"en","permalink":"https://martin-thurau.de/story/esailors/","title":"eSailors IT Solutions GmbH","path":"/opt/buildhome/repo/content/story/esailors.md"},{"lang":"de","permalink":"https://martin-thurau.de/de/story/esailors/","title":"eSailors IT Solutions GmbH","path":"/opt/buildhome/repo/content/story/esailors.de.md"}],"backlinks":[]},{"relative_path":"story/keeeb.md","colocated_path":null,"content":"<p>keeeb was initially developed as a platform to collect and share information (think Evernote/Google Keep/etc.) on the\nweb. After a pivot we focused more on the enterprise sector as a tool for collective information gathering and knowledge\nstorage. I worked here part time during my time as a student and later as a full-time developer.<span id=\"continue-reading\"></span></p>\n<h2 id=\"cross-browser-extension-to-collect-information\">Cross browser extension to collect information</h2>\n<p>keeeb as a information collection software required a way for users to actually collect things they are interested in\nwhile browsing the web. When I initially joined this was done through a bookmarklet that would inject Javascript and CSS\ninto the site and they present a simple UI to collect snippets. I migrated this to a real browser extension that was\nwritten with the help of the Kango Framework which allowed writing an extension that would run in all major browsers\nwithout the need to do a rewrite for each of them.</p>\n<h2 id=\"cross-platform-mobile-app\">Cross platform mobile app</h2>\n<p>Given the small team size and tight money constraints we simply could not have dedicated developers to build mobile\napplications. Because of this I built an app for Android and iOS that used Phonegap as a base and Backbone.js for the\nrendering. Given how junior I was at the time I’m very proud of past-me how well the app performed.</p>\n<h2 id=\"frontend-rewrite\">Frontend rewrite</h2>\n<p>As a replacement for our old, static frontend we designed and implemented a completely new one. This frontend was built\nas a single page application based on AngularJS. My work involved initial design and implementation. Later I worked on\nadding new features and maintenance. I also designed and implemented most parts of our build system (based on Gulp) to\nensure easy development and clean deployment.</p>\n<h2 id=\"day-to-day-maintenance\">Day to day maintenance</h2>\n<p>I also did some maintenance work on our Pyramid based backend servers and implemented new features. During this time I\nhelped with the migration from MongoDB to PostgreSQL. We also added a full-text search based on Elasticsearch and\nmigrated our backend job-system from a homebrew version to something based on Celery and RabbitMQ.</p>\n<h2 id=\"director-of-development\">Director of development</h2>\n<p>Due to some changes in the company structure I was also asked to take over the role of “Director of development”. The\nwork here involved planning and designing of new features as well as project management and organisation of our\ndevelopment team.</p>\n","permalink":"https://martin-thurau.de/story/keeeb/","slug":"keeeb","ancestors":["_index.md","story/_index.md"],"title":"keeeb GmbH","description":null,"updated":null,"date":null,"year":null,"month":null,"day":null,"taxonomies":{"technologies":["Angular","AngularJS","Kubernetes","Protractor","Jasmine","git"],"languages":["Typescript","Java","Javascript"]},"authors":[],"extra":{"work_from":"2013-04-01","work_until":"2016-10-30","logo":"story/keeeb_logo.png","link":"https://www.keeeb.com/"},"path":"/story/keeeb/","components":["story","keeeb"],"summary":"<p>keeeb was initially developed as a platform to collect and share information (think Evernote/Google Keep/etc.) on the\nweb. After a pivot we focused more on the enterprise sector as a tool for collective information gathering and knowledge\nstorage. I worked here part time during my time as a student and later as a full-time developer.","toc":[{"level":2,"id":"cross-browser-extension-to-collect-information","permalink":"https://martin-thurau.de/story/keeeb/#cross-browser-extension-to-collect-information","title":"Cross browser extension to collect information","children":[]},{"level":2,"id":"cross-platform-mobile-app","permalink":"https://martin-thurau.de/story/keeeb/#cross-platform-mobile-app","title":"Cross platform mobile app","children":[]},{"level":2,"id":"frontend-rewrite","permalink":"https://martin-thurau.de/story/keeeb/#frontend-rewrite","title":"Frontend rewrite","children":[]},{"level":2,"id":"day-to-day-maintenance","permalink":"https://martin-thurau.de/story/keeeb/#day-to-day-maintenance","title":"Day to day maintenance","children":[]},{"level":2,"id":"director-of-development","permalink":"https://martin-thurau.de/story/keeeb/#director-of-development","title":"Director of development","children":[]}],"word_count":393,"reading_time":2,"assets":[],"draft":false,"lang":"en","lower":null,"higher":null,"translations":[{"lang":"de","permalink":"https://martin-thurau.de/de/story/keeeb/","title":"keeeb GmbH","path":"/opt/buildhome/repo/content/story/keeeb.de.md"},{"lang":"en","permalink":"https://martin-thurau.de/story/keeeb/","title":"keeeb GmbH","path":"/opt/buildhome/repo/content/story/keeeb.md"}],"backlinks":[{"permalink":"https://martin-thurau.de/story/master/","title":"Universität zu Lübeck"}]},{"relative_path":"story/komoot.md","colocated_path":null,"content":"<p>komoot is a mobile app and website for navigation and route planning for outdoor activities. It was founded in 2010 and has, as of March 2025, over 7 million monthly active users (45 million total) and is the leading outdoor app in Germany and other European countries.</p>\n<p>I joined komoot as a Senior Backend Engineer in October 2021 and started in the Monetization squad, which handled all payment related areas as well as owning most of the premium features. In late 2024, I switched to the &quot;Developer Productivity&quot; squad, which was founded out of internal need from the backend team.</p>\n<p>In March 2025 the company was bought by the Italian technology company Bending Spoons which laid off a large percentage of employees, including myself, in early April.</p>\n<span id=\"continue-reading\"></span>\n<p>The tech stack of komoot had no arbitrary limitations; we generally used what was best for the given task. However, for the sake of maintainability, we usually used the following components:</p>\n<ul>\n<li>AWS as a cloud platform</li>\n<li>Databases: MySQL (for historical reasons), PostgreSQL (for anything GIS related), and DynamoDB</li>\n<li>Languages: Kotlin or Python</li>\n<li>Frameworks: Spring (Boot) and ktor</li>\n<li>Runtime: AWS Fargate or AWS Lambda functions</li>\n<li>CI/CD: CircleCI or GitHub Actions</li>\n</ul>\n<h2 id=\"work-in-the-monetization-squad\">Work in the Monetization squad</h2>\n<h3 id=\"migrating-welcome-offer-emails-database\">Migrating welcome offer emails database</h3>\n<p>komoot was sending out welcome offer emails to users who joined. That offer consisted of a ~60% discount on their &quot;World Pack&quot; product, which allows users to navigate offline and download map data of the entire world. \nThe original implementation of this was roughly 5 years old and very inflexible. As the Monetization squad wanted to extend the offer to also support the subscription-based &quot;komoot Premium&quot; there was some work needed to make this possible. This was also my onboarding project at komoot, to familiarize myself with the main codebase and the central, monolithic core.</p>\n<p>As part of this change, I migrated the live data from the existing storage in MySQL to a more scalable, DynamoDB-based storage. This involved an ongoing data replication with a dual-write strategy, paired with a background job moving the existing data. I also implemented sending different emails depending on user type and feature flags.</p>\n<h3 id=\"mass-mail-sending\">Mass mail sending</h3>\n<p>For general newsletter sending, I developed a new mass mailing system. The existing one was very simple and relied on loading all necessary data into memory, which was now no longer possible due to the sheer size of the user base. It also had other technical problems, so we decided not to fix it but to replace it. Since personalized email sending and templating were already built as an in-house solution, it was easier to just add mass-mailing on top instead of transitioning to an external vendor.</p>\n<p>The system worked by loading data from an Athena export of the relevant user data and then using an SQS queue to invoke a Lambda function to send out the emails. All tied together by an AWS Step function for management and coupled to a simple UI so that people from the brand and other squads could send newsletters as a self-service.</p>\n<h3 id=\"dynamic-promotions\">Dynamic Promotions</h3>\n<p>In the past, komoot always struggled with sales campaigns. Usually, those campaigns would contain different offers for new users, users who had already purchased a one-time product, and people with expired subscriptions. The root of the problem was that there were multiple places where logic was implemented with regard to which offer a particular user got: the purchase backend, the campaign email sending, and then also in each of the clients. Sometimes, those criteria were not quite lining up for edge cases, and there were changes over time (e.g., a subscription ran out shortly after/before sending an offer email). Additionally, most of the logic was hardcoded and also had to be hand-changed from campaign to campaign since we also usually did changes <em>and</em> ran A/B tests.</p>\n<p>Given that we were usually sending out between 12 and 20 million emails per campaign, even a very small fraction of these issues would result in hundreds of questions to our support regarding prices being not what we promised in a specific email, which then also required manual work to make the promised prices work for users who were really insisting.</p>\n<p>We also had two entirely different concepts of promotions available to users. One was the aforementioned sales campaigns, which would happen on a schedule determined by us. But users were also getting individualized offers for upselling or as win-back offers on subscription cancellation. </p>\n<p>&quot;Dynamic Promotions&quot; was designed to address those problems once and for all. The core idea was to provide an explicit configuration (instead of code) for any promotion we wanted to run. That would then be evaluated once for each user, and all decisions would be made atomically, persisted, and then seen as the source of truth for every other decision to be made. It worked by:</p>\n<ul>\n<li>providing a JSON-based configuration detailing the entire campaign or promotion</li>\n<li>gathering all the required user details to make a decision</li>\n<li>persisting that decision</li>\n<li>generating and scheduling all future communication (e.g. emails or push notifications) </li>\n</ul>\n<p>This operation would either run as a batch job for <em>all</em> users or be triggered by events (e.g. &quot;If the user purchases a maps package, offer them a discounted premium subscription for 2 weeks and send two emails&quot;). The promotions would also contain details on the in-app communication, which was provided as an API to all clients so they could display messaging accordingly.</p>\n<p>The entire system was built around some simple Lambda functions, DynamoDB for storage, and a pretty large AWS Step Function for processing all users and events in a common way. We also built in various testing capabilities, like a dry-run mode to validate entire campaigns or a testing CLI for our QA team to test individual promotions even without the need to execute the required user actions. </p>\n<p>The system worked remarkably well and was over time adapted and expanded to more use cases and is still the main driver for promotions at komoot.</p>\n<h3 id=\"handover-to-me\">Handover to me</h3>\n<p>A few months after I joined, the other backend developer in the squad had to leave the company for personal reasons. While this would unfortunately leave me as the sole backend developer in the squad, we quickly figured out a plan for a good handover. For this, we sat together and assembled a list of all repositories, features, and documentation related to the ownership of the Monetization squad. Over a couple of months, I then dedicated multiple hours a week to study the code and docs in detail. Each week, we would come together, and I would walk through all the open questions. As a result of this, I then improved the code or docs in question by refactoring them, adding more code comments, or (re)writing documentation. </p>\n<p>This process proved very effective, so I was able to take over the sole developer role in the squad without feeling I had big knowledge gaps. The improved documentation also proofed really useful when a new developer joined the Monetization squad since later on.</p>\n<h3 id=\"a-b-tests\">A/B tests</h3>\n<p>In the Monetization squad, we obviously had the goal to improve revenue. For this reason, we ran many A/B tests over the years, most of them requiring some form of support from the backend. Some noticeable tests we did are:</p>\n<ul>\n<li>Insurance removal: We experimented how to remove the bike insurance from the komoot premium subscription offering before actually implementing it.</li>\n<li>Different shop display: Depending on various factors, we experimented with showing the user different products as the first, big thing on the &quot;shop&quot; page. Either the user was shown the premium product first or the maps products. </li>\n<li>Price tests: We experimented with various prices for the subscription product.</li>\n</ul>\n<h3 id=\"sales-campaigns\">Sales campaigns</h3>\n<p>At komoot, we ran various sales campaigns over the year. Usually, we would do a short one for the new year, one in summer during peak season, and then another one in early autumn. The campaigns were always involving different price points, user selections, and communication styles. Before we introduced the &quot;Dynamic Promotions&quot; system, we would often have to change or adjust backend logic to support the various ideas that the PM and designer came up with.</p>\n<h3 id=\"insurance-removal\">Insurance removal</h3>\n<p>The premium offering of komoot included bike insurance (backed by the German insurer AXA) at no extra cost. While the offer was always included in each subscription, users had to do some manual steps to activate it, most importantly provide their personal address since that was required by AXA. For this to work seamlessly, there existed a relatively complicated two-way sync between our applications and an AXA API. </p>\n<p>In 2022, the decision was made to remove the offering from the premium subscription. This was relatively complicated because we had to make sure the offering was no longer included in new and renewing subscriptions while also making sure it would continue to work for any existing subscription that was created before a specific cut-off date. Because of that, the final removal of the code could only happen a full year after we stopped the offering.</p>\n<h3 id=\"supporting-new-countries-and-prices\">Supporting new countries and prices</h3>\n<p>komoot classified the countries where we operated into different tiers. Tier 1 countries had full localization into the native languages and also prices were localized into the respective local currencies.\nIn 2022, komoot added support for Poland, Brazil, Japan, and South Korea to the list of Tier 1 countries, so the monetization features all needed to support this. This was a particularly interesting endeavor since, while komoot never made the mistake of storing monetary values as floating-point numbers, all code <em>did</em> assume that all monetary values are stored in minor currency units (e.g., cents) and that every currency has 100 minor currency units for each major currency unit. An assumption that is not true for South Korean Won and Japanese Yen. That required a bunch of refactoring to make everything work properly.</p>\n<h3 id=\"free-experience-and-moving-feature-behind-a-paywall\">Free experience and moving feature behind a paywall</h3>\n<p>In 2024, we wanted to make certain features a paid feature for new users. komoot generally tried to avoid taking things away, so this project involved figuring out where the various checks were made and adjusting them so they would continue to work for existing users but require a valid subscription for new users.</p>\n<p>Paired with that, we also added the capabilities for a &quot;free experience&quot; for those features where new users could use them for free for a while but then are presented with a paywall and a limited-time offer for a subscription. </p>\n<h3 id=\"smaller-projects\">Smaller projects</h3>\n<p>Apart from the already mentioned projects in the Monetization squad, I was also involved in various smaller projects:</p>\n<ul>\n<li>Improving business events to the analytics team to add more information.</li>\n<li>Lots and lots of bug fixes.</li>\n<li>Enabling komoot team members to buy premium vouchers at a discounted price for family and friends.</li>\n<li>VAT and price-related changes.</li>\n<li>Automated refund process for subscriptions in our customer support backend with the click of a button.</li>\n</ul>\n<h2 id=\"work-in-the-backend-team\">Work in the backend team</h2>\n<p>While my primary role was inside the Monetization squad, we also had time to work on non-product-related things in the backend team itself. This was usually either done on &quot;Quality Monday&quot; or as part of the quarterly team OKR weeks where we tackle more technical topics that did not fit into the squads' mission. Some noteworthy examples of this are:</p>\n<h3 id=\"hiring\">Hiring</h3>\n<p>In 2023, I also joined the hiring group for the backend team. Our hiring process involved a coding challenge sent to each applicant and then 2 separate interviews with 2 people each. The first, technical interview would focus on the technical capabilities, while the second was more focused on product and feature development. I was involved in reviewing challenge submissions from applicants and evaluating them against a set catalog of criteria and questions. I also was part of the group of people who handled the first, technical interview.</p>\n<p>After each round of interviews, we four people would come together and make a group decision on whether or not we would make the candidate an offer. </p>\n<h3 id=\"cookiecutter-template\">Cookiecutter template</h3>\n<p>To shorten the time that is needed to spin up a new microservice or lambda function within the backend team, I implemented a Cookiecutter template repository for both use cases. This was based on an existing, simple template repository that would just replace placeholders, but that required a lot of manual work until the new stack could first be deployed. </p>\n<p>I implemented two variations that also used a shared part to have a generator that could fully create two kinds of commonly used repositories:</p>\n<ul>\n<li>A lambda function with options support for SQS and DynamoDB</li>\n<li>An ECS Fargate-based service using ktor</li>\n</ul>\n<p>Both templates were ready-to-use after generation, so they could be directly deployed (albeit without any functionality). I also added automated integration testing to the template repository that made sure that all possible variations would generate a valid project. The integration test also really deployed it into a dedicated AWS account for verification and called the resulting Lambda/ECS service.</p>\n<h3 id=\"fargate-migration-of-monolith\">Fargate migration of monolith</h3>\n<p>The central monolith of komoot had been running on plain EC2 instances for a long time. The scaling and deployment was managed via <a href=\"https://github.com/boxfuse\">boxfuse</a>. We decided to migrate it to an ECS Fargate service to be able to run more, smaller instances and to scale up and down faster, as well as reducing operation costs slightly. </p>\n<p>This project involved setting up a new Fargate service for the monolith, implementing the autoscaling, migrating all metrics and alarms, and was completed without any downtime or user impact within a month.</p>\n<h3 id=\"on-call-processes\">On-call processes</h3>\n<p>I improved the processes around our on-call scheduling in multiple ways. I wrote a utility in Rust that would automatically detect upcoming conflicts between the on-call rotation and vacations entered in our employee management software, <a href=\"https://www.hibob.com/\">Bob</a>. Those would then automatically be sent to the users which caused the conflict so they could take care of finding someone to swap with.</p>\n<p>Late in 2024, we also introduced compensation for being on-call. For each day being on-call, you would be attributed time-off. These days could either be taken as paid time off or, if not taken, would automatically be paid out after 3 months. I implemented some Python-based code around this to fully automate the process of keeping track of earned time per user and also sending them over to payroll for payout.</p>\n<h3 id=\"backend-hub-documentation\">&quot;Backend-hub&quot; documentation</h3>\n<p>The backend team had various documents scattered all over the place. From onboarding and generic setup instructions over on-call handbooks to other non-system-specific documentation. Most of it was in the form of Google Docs files, but then there were also presentations, README files, and various other resources.</p>\n<p>I took the time to set up a dedicated hub for all backend-related documentation into a dedicated GitHub repository that is generating an intranet-accessible, MkDocs-based set of pages that can easily be navigated and browsed. Over time, I migrated over all the critical documentation together with the team.</p>\n<h2 id=\"api-test-migrations-and-improvements\">API test migrations and improvements</h2>\n<p>komoot was originally using the <a href=\"https://robotframework.org/\">Robot Framework</a> for API-based integration tests. Over time, those became hard to scale, so years before I joined, the decision was made that new tests would be written as plain Python-based tests.\nI took the initiative to migrate most of the still-existing Robot tests over to the Python tests so we could benefit from better parallelization and also save the additional step in our CI pipeline. Most of the work was done in my first month, partly to familiarize myself with the existing API and functionalities, while the remainder was cleaned up over the years.</p>\n<p>I also spent time on-and-off to stabilize the existing API tests by adding retry logic, fixing race conditions, and some odd bugs that always existed but only resulted in sometimes flaky tests. </p>\n<h2 id=\"quality-mondays\">Quality Mondays</h2>\n<p>At komoot, each Monday was dedicated to non-critical bug fixes and minor improvements inside the backend code. Each Monday, we would gather together in a Zoom call to look at bug reports (already prepped by our QA team) and metrics-related warnings from the previous week and try to solve them (or push them down into the product squads). The assignment of bugs was done by ourselves, so over the years, I worked on almost all parts of komoot in one way or another.</p>\n<h2 id=\"work-in-the-developer-productivity-squad\">Work in the &quot;Developer Productivity&quot; squad</h2>\n<p>The &quot;Developer Productivity&quot; squad was formed as an initiative from within the backend team in late 2024. We all wanted more time to be spent on technical tasks that would benefit everyone. To facilitate this need, we formed this new squad with two developers as a mid-term experiment to see how viable that idea is. </p>\n<h3 id=\"increase-pipeline-speed\">Increase pipeline speed</h3>\n<p>The first task we tackled was an effort into the pipeline speed of our core monolith. It was quite a large application, so it had a huge number of unit, integration, and API tests. Running this pipeline for a fresh pull request would take over 45 minutes, while a simple production deployment was almost 30 minutes. Since a lot of time was spent working on this codebase by multiple developers, each minute reduced in the CI step would greatly improve overall iteration speed.</p>\n<p>We spent some time making the general build faster by cutting off unnecessary steps, introducing more parallelism, and generally speeding up individual steps.</p>\n<p>Another chunk of time was spent on making the per-branch stacks deployment faster. It originally was using database snapshots to create the two main databases for the application, which took a long time since the snapshots were quite large. We solved this by instead using empty databases and then introducing test fixtures to set up the database schema and content.</p>\n<p>Overall, our efforts brought down the production deployment to 18 minutes and the branch-stack creation to roughly 22 minutes.</p>\n<h3 id=\"feature-flagging\">Feature flagging</h3>\n<p>My last task at komoot was implementing a feature flagging and experimentation platform for all applications and clients. Prior to this, we only had an in-house experimentation platform for A/B tests, and the backend had some remote feature flagging. We wanted to unify the feature flagging and experimentation on one platform and also provide a common, remote feature flagging solution to all backend services, the web frontend, and mobile apps.</p>\n<p>For this, we deployed <a href=\"https://www.growthbook.io/\">GrowthBook</a> to our AWS account and integrated it with our data warehouse and event pipeline. I also added the integration to all relevant backend services, as well as providing support and documentation for the client-side integration.</p>\n<p>Sadly, this project was never fully completed since in March 2025 komoot was acquired by Bending Spoons, which stopped all current projects and let go of many developers.</p>\n","permalink":"https://martin-thurau.de/story/komoot/","slug":"komoot","ancestors":["_index.md","story/_index.md"],"title":"komoot GmbH","description":null,"updated":null,"date":null,"year":null,"month":null,"day":null,"taxonomies":{"technologies":["Spring Boot","CloudFormation","ktor","AWS Lambda"],"languages":["Kotlin","Java","Python","Rust"]},"authors":[],"extra":{"work_from":"2021-10-01","link":"https://komoot.com","logo":"story/komoot_logo.png"},"path":"/story/komoot/","components":["story","komoot"],"summary":"<p>komoot is a mobile app and website for navigation and route planning for outdoor activities. It was founded in 2010 and has, as of March 2025, over 7 million monthly active users (45 million total) and is the leading outdoor app in Germany and other European countries.</p>\n<p>I joined komoot as a Senior Backend Engineer in October 2021 and started in the Monetization squad, which handled all payment related areas as well as owning most of the premium features. In late 2024, I switched to the &quot;Developer Productivity&quot; squad, which was founded out of internal need from the backend team.</p>\n<p>In March 2025 the company was bought by the Italian technology company Bending Spoons which laid off a large percentage of employees, including myself, in early April.</p>\n","toc":[{"level":2,"id":"work-in-the-monetization-squad","permalink":"https://martin-thurau.de/story/komoot/#work-in-the-monetization-squad","title":"Work in the Monetization squad","children":[{"level":3,"id":"migrating-welcome-offer-emails-database","permalink":"https://martin-thurau.de/story/komoot/#migrating-welcome-offer-emails-database","title":"Migrating welcome offer emails database","children":[]},{"level":3,"id":"mass-mail-sending","permalink":"https://martin-thurau.de/story/komoot/#mass-mail-sending","title":"Mass mail sending","children":[]},{"level":3,"id":"dynamic-promotions","permalink":"https://martin-thurau.de/story/komoot/#dynamic-promotions","title":"Dynamic Promotions","children":[]},{"level":3,"id":"handover-to-me","permalink":"https://martin-thurau.de/story/komoot/#handover-to-me","title":"Handover to me","children":[]},{"level":3,"id":"a-b-tests","permalink":"https://martin-thurau.de/story/komoot/#a-b-tests","title":"A/B tests","children":[]},{"level":3,"id":"sales-campaigns","permalink":"https://martin-thurau.de/story/komoot/#sales-campaigns","title":"Sales campaigns","children":[]},{"level":3,"id":"insurance-removal","permalink":"https://martin-thurau.de/story/komoot/#insurance-removal","title":"Insurance removal","children":[]},{"level":3,"id":"supporting-new-countries-and-prices","permalink":"https://martin-thurau.de/story/komoot/#supporting-new-countries-and-prices","title":"Supporting new countries and prices","children":[]},{"level":3,"id":"free-experience-and-moving-feature-behind-a-paywall","permalink":"https://martin-thurau.de/story/komoot/#free-experience-and-moving-feature-behind-a-paywall","title":"Free experience and moving feature behind a paywall","children":[]},{"level":3,"id":"smaller-projects","permalink":"https://martin-thurau.de/story/komoot/#smaller-projects","title":"Smaller projects","children":[]}]},{"level":2,"id":"work-in-the-backend-team","permalink":"https://martin-thurau.de/story/komoot/#work-in-the-backend-team","title":"Work in the backend team","children":[{"level":3,"id":"hiring","permalink":"https://martin-thurau.de/story/komoot/#hiring","title":"Hiring","children":[]},{"level":3,"id":"cookiecutter-template","permalink":"https://martin-thurau.de/story/komoot/#cookiecutter-template","title":"Cookiecutter template","children":[]},{"level":3,"id":"fargate-migration-of-monolith","permalink":"https://martin-thurau.de/story/komoot/#fargate-migration-of-monolith","title":"Fargate migration of monolith","children":[]},{"level":3,"id":"on-call-processes","permalink":"https://martin-thurau.de/story/komoot/#on-call-processes","title":"On-call processes","children":[]},{"level":3,"id":"backend-hub-documentation","permalink":"https://martin-thurau.de/story/komoot/#backend-hub-documentation","title":"\"Backend-hub\" documentation","children":[]}]},{"level":2,"id":"api-test-migrations-and-improvements","permalink":"https://martin-thurau.de/story/komoot/#api-test-migrations-and-improvements","title":"API test migrations and improvements","children":[]},{"level":2,"id":"quality-mondays","permalink":"https://martin-thurau.de/story/komoot/#quality-mondays","title":"Quality Mondays","children":[]},{"level":2,"id":"work-in-the-developer-productivity-squad","permalink":"https://martin-thurau.de/story/komoot/#work-in-the-developer-productivity-squad","title":"Work in the \"Developer Productivity\" squad","children":[{"level":3,"id":"increase-pipeline-speed","permalink":"https://martin-thurau.de/story/komoot/#increase-pipeline-speed","title":"Increase pipeline speed","children":[]},{"level":3,"id":"feature-flagging","permalink":"https://martin-thurau.de/story/komoot/#feature-flagging","title":"Feature flagging","children":[]}]}],"word_count":3156,"reading_time":16,"assets":[],"draft":false,"lang":"en","lower":null,"higher":null,"translations":[{"lang":"en","permalink":"https://martin-thurau.de/story/komoot/","title":"komoot GmbH","path":"/opt/buildhome/repo/content/story/komoot.md"},{"lang":"de","permalink":"https://martin-thurau.de/de/story/komoot/","title":"komoot GmbH","path":"/opt/buildhome/repo/content/story/komoot.de.md"}],"backlinks":[]}],"page_count":4}