<?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[Agoda Engineering &amp; Design - Medium]]></title>
        <description><![CDATA[Learn about how products are developed at Agoda, what is being done under the hood, from engineering to design, to provide users a seamless experience at agoda.com. - Medium]]></description>
        <link>https://medium.com/agoda-engineering?source=rss----9bca683764b5---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Agoda Engineering &amp;amp; Design - Medium</title>
            <link>https://medium.com/agoda-engineering?source=rss----9bca683764b5---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 13:57:10 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/agoda-engineering" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Key Takeaways from Tech at Agoda: From Assistants to AI Agents]]></title>
            <link>https://medium.com/agoda-engineering/key-takeaways-from-tech-at-agoda-from-assistants-to-ai-agents-b0e9187730bb?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/b0e9187730bb</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Tue, 27 Oct 2026 04:04:00 GMT</pubDate>
            <atom:updated>2026-10-27T04:03:59.401Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lV7RmGu4chwSLUErtGKuuA.jpeg" /></figure><p>AI agents are no longer just suggesting the next line of code. They’re starting to plan, build, test, and ship work alongside engineers. Last month, we explored that shift in the <a href="https://medium.com/agoda-engineering/key-takeaways-from-agodas-ai-developer-report-2026-82915b88c370">Agoda AI Developer Report 2026</a>. On 21 October 2026, we took the conversation further by inviting Bangkok’s tech community to our office at One Bangkok for an evening of talks and discussions. Our theme was “From Assistants to AI Agents,” and leaders from Agoda, Microsoft, SCB 10X, OpenAI, and Omise shared where agents are already making a difference, where the bottlenecks have moved, and how teams can stay in control.</p><p>Here are the key takeaways.</p><h3>1. Speed moves the bottleneck</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9TzD0ql3ydBl_jWDRR-6-Q.jpeg" /></figure><p>AI coding tools have made writing code dramatically faster, but delivery hasn’t kept pace. Once coding gets faster, the slow parts shift to planning, review, testing, release, and understanding what customers actually need. The teams seeing the biggest gains are the ones looking at the whole lifecycle, not just the editor.</p><h3>2. AI belongs in every stage, not just coding</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*irez8OEtPouPW9IsX3BV4Q.jpeg" /></figure><p>The next gains come from applying AI beyond code generation: reviewing changes, triaging support requests, checking specs before work begins, and running agents for hours in the cloud rather than on a laptop. As these tools start working together, the engineer’s role shifts from handing out tasks to setting goals and reviewing results.</p><h3>3. Good context and clear goals make agents useful</h3><p>Agents perform best when they’re treated like new team members. They need clear instructions, curated context, and a precise definition of what “done” looks like. A vague goal or a loose definition of “good” leads agents to repeat one pattern everywhere, or even to optimize for the score instead of the outcome. Framing the goal well and adding independent checks matter as much as the model itself.</p><h3>4. Match autonomy to risk</h3><p>Not every decision needs a human, but some always should. A useful test is whether a mistake can be reversed and how much damage it could do. Low-risk, reversible work can be delegated, while high-stakes or irreversible decisions should be paused for a person. Many teams start small, automating narrow, well-defined tasks and keeping a human approval step before anything goes live.</p><h3>5. Accountability stays with people</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uCF_DyFWFArhkgoa-jw1GA.jpeg" /></figure><p>Agents can take on more of the work, but they can’t take on the responsibility. When AI writes more code than anyone can read line by line, teams need agents that flag where the risk lies, plus strong observability and audit trails. Rather than making engineering skills less important, agents make sound judgment and fundamentals matter more.</p><h3>Conclusion</h3><p>AI agents are already changing how software gets built, but the evening made one thing clear: the biggest gains don’t come from faster code alone. They come from rethinking the whole delivery process, giving agents the right context and goals, and keeping people in charge of the decisions that matter. As agents take on more of the work, engineering judgment becomes more valuable, not less.</p><p>We’re continuing to explore what this shift means for how we build at Agoda, and we look forward to sharing more of what we learn with the engineering community. Thank you to our speakers, panelists, and everyone who joined us in Bangkok.</p><p><strong>Explore opportunities in our Tech team today!</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b0e9187730bb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/key-takeaways-from-tech-at-agoda-from-assistants-to-ai-agents-b0e9187730bb">Key Takeaways from Tech at Agoda: From Assistants to AI Agents</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How We Cut the Coupling Tax in a Post-Booking API]]></title>
            <link>https://medium.com/agoda-engineering/how-we-cut-the-coupling-tax-in-a-post-booking-api-2156a3175538?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/2156a3175538</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Tue, 20 Oct 2026 04:51:48 GMT</pubDate>
            <atom:updated>2026-10-20T04:51:48.468Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Thanaphoom Pungchaichan, Protim Mallick, Abdullah Al Tariq, Prashant Mohta</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vbg9pZUF5iHCJqc6ie_w9g.png" /></figure><p>At Agoda, our post-booking systems support complex flows involving cancellations, amendments, refunds, and customer communications. These flows are coordinated through a write-oriented orchestration service that we will call <strong>“BAPI”.</strong></p><p>As BAPI handled more traffic and attracted more contributions, its codebase became increasingly difficult to change. Responsibilities that should have remained separate grew tightly connected, so even a small update could require a broad understanding of the system and more effort to review safely.</p><p>Rewriting the service from scratch was not an option. Instead, we created the Platform framework, a modular architecture designed to introduce clear and enforceable boundaries into BAPI while allowing feature development to continue.</p><p>In this blog post, we share how we adopted the framework incrementally, enforced its rules in CI, and measured its relationship with review speed and AI-assisted development.</p><h3>Why we built the platform framework</h3><p>In 2019, we had to decide what to do with an aging core monolith. But as Agoda’s business grew rapidly, it became increasingly challenging to maintain the core of our business within a single monolith. Hence, we decided to split our domain into two parts: booking creation and post-booking. Thus, the application BAPI was born. To make BAPI a reality, we chose a deliberately pragmatic path: to extract and translate the post-booking business line by line into a new application, without attempting a broader redesign. The expectation was that a separate application would give our postbooking system plenty of headroom to grow.</p><p>The approach worked. In the language of John K. Ousterhout’s <a href="https://web.stanford.edu/~ouster/cgi-bin/aposd.php">A Philosophy of Software Design</a>, we had deliberately traded a strategic design approach for a tactical one to prioritize speed to market. This tactical mindset was exactly what the business needed at the time. The service ran smoothly, features shipped rapidly, and operations remained uninterrupted. For several years, it proved to be the right decision.</p><p>Over time, however, the limitations of the line-by-line migration became harder to ignore. After we deprecated the legacy service and rerouted its remaining traffic to BAPI, the service saw increased traffic and attracted more contributions. The number of merge requests more than quadrupled between 2022 and 2025.</p><p>Without strong architectural guardrails, the codebase expanded along the paths of least resistance. Over time, the legacy codebase grew substantially. Some files exceeded 4,000 lines, and changing one often required understanding a large web of connected behavior.</p><p>The original structure used a traditional package-by-layer design, organizing packages around separate technical concerns. This approach followed the classic <a href="https://martinfowler.com/bliki/PresentationDomainDataLayering.html">Presentation-Domain-Data Layering</a> pattern described by Martin Fowler. In practice, however, using technical layers as the top-level organization at our scale meant the boundaries were not strict enough. The server module consolidated business logic, repository access, controllers, mappers, and utilities, while the common module served as a destination for anything that seemed reusable.</p><p>This was not a failure of contributor judgment. The architecture did not provide enough guidance to make boundary decisions consistently.</p><p>Developers felt the impact in their day-to-day work. Changes took longer to review, tests were more likely to break, and pipelines became less reliable. Even small fixes started to feel riskier than they should have.</p><h3>The decision: platform framework</h3><p>We faced two constraints. We could not rewrite the entire system because the business could not pause feature development for several years. We also could not leave the architecture unchanged while development velocity continued to decline.</p><p>The Platform framework gave us a third option. Rather than replacing the legacy system at once, we introduced a <strong>modular monolith </strong>architecture with strict isolation rules. This time, we deliberately chose a <strong>strategic design approach</strong> over a tactical one. We enforced new modules to use the framework by default, while legacy areas migrate gradually as substantial feature work requires changes.</p><p>The goal was to reverse the existing incentive structure. The old codebase made tight coupling the path of least resistance. The Platform framework needed to make the well-structured path easier than taking a shortcut. By hiding structural complexity inside the framework itself, we wanted to lower the cognitive load on contributors so they could change code with confidence.</p><p>Our mental model was simple: grow a new skeleton beside the old system and allow it to carry more of the workload over time.</p><h3>How the platform framework works</h3><p>The core shift was moving away from traditional <a href="https://martinfowler.com/bliki/PresentationDomainDataLayering.html">package-by-layer</a> boundaries. Instead of organizing code horizontally by technical concerns, we adopted a vertical <a href="https://simonbrown.je/modular-monolith/">package-by-component</a> pattern to build a <strong>modular monolith</strong>.</p><p>We organize our codebase into coarse-grained, isolated modules centered around specific business domains — such as `rebook`, `cancellation`, and `payment`. If business domains do not overlap conceptually, their code should not overlap either. For example, cancellation logic belongs strictly inside the cancellation component and cannot bypass boundaries to depend directly on communication or loyalty logic.</p><p>Each domain component acts as an encapsulated container that bundles its business logic and data access together behind a clean public interface. Internally, to ensure the code inside a component stays highly organized and uniform across the team, the framework applies a strict layered architecture:</p><ul><li><strong>Application layer</strong>: Orchestrates the request. It knows what to do, but not how.</li><li><strong>Service layer</strong>: Owns the business logic for one domain. It knows how to act on the data.</li><li><strong>Repository layer: </strong>Owns data access and never exposes its internal models to other layers<strong>.</strong></li><li><strong>Model layer: </strong>Contains the domain data structures shared across the other layers.</li></ul><p>Determining these layers and how they interact with each other was not a trivial task. We intentionally set up strict boundary rules so that the day-to-day work within each module remains as predictable and brainless as possible. Again, we found our guiding rationale in <a href="https://web.stanford.edu/~ouster/cgi-bin/aposd.php">John K. Ousterhout</a>:</p><blockquote><em>“Complexity is determined by the activities that are most common. If a system has a few parts that are very complicated, but those parts almost never need to be touched, then they don’t have much impact on the overall complexity of the system.”</em></blockquote><p>By standardizing our module internals into an application, service, and repository skeleton, we isolated the structural complexity within the framework setup itself, a part of the codebase our contributors rarely need to modify. In return, the tasks our developers most commonly perform—adding features, changing business logic, or writing queries — occur in isolated, highly predictable places where cognitive load is minimized.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JQzbd9SbftUPu9oDGMo4ew.png" /><figcaption><em>Figure 1: Platform framework boundaries inside a domain module.</em></figcaption></figure><p>The most important rule is that services and repositories do not communicate directly, and neither do sibling services. Application-level components, including startup classes, middleware, handlers, and contexts, mediate the request flow.</p><p>At a simplified level, the framework looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nXlmqf2b9IW9EIRbZPXstg.png" /></figure><p>This prevents a change on one side of a boundary from silently spreading across the module. The value lies less in the layer names than in the prohibition on shortcuts.</p><p>Two mechanisms are especially important: Data Mapper prevents repository internals from leaking upward, while typed builders make object creation explicit and searchable.</p><h3>Data Mapper: translation at the boundary</h3><p>The framework follows Martin Fowler’s <a href="https://martinfowler.com/eaaCatalog/dataMapper.html">Data Mapper</a> pattern. Repository-specific models, including API payloads, database entities, and cache objects, never leave the repository layer. Mappers translate them into domain models before another layer can use them.</p><p>This keeps the domain layer independent of the underlying data source. A repository implementation can change without forcing corresponding changes throughout the business logic.</p><h3>Builders: bounded and searchable</h3><p>We use typed builders to keep object creation and transformation within clear boundaries. In the legacy code, an object could pass from a handler to a service, then to a repository, and back again, changing at several points along the way. A .copy() in one layer, a helper in another, and another transformation in a mapper meant we had to search for the type name and hope we had not missed anything.</p><p>In the Platform framework, objects can only be created through builders tied to a specific use case. We have separate builders for service inputs, domain models, and responses, and each one keeps the relevant transformations in one place.</p><p>If we want to see all the ways a Booking domain model can be created, we look in BookingDomainBuilder. To find where a service input is created, we search for its ServiceInputBuilder. We no longer have to trace a chain of changes across the entire request flow.</p><h3>Encouraging incremental adoption</h3><p>Designing the framework was only part of the work. We also needed a way to introduce it without pausing feature development.</p><p>Rather than migrate BAPI all at once, we made the Platform framework the default for new modules and submodules. We move existing areas when feature work gives us a practical reason to change them. Over time, several post-booking domains have begun building directly on the shared platform module.</p><p>The framework encourages adoption by making common work easier within its boundaries. It provides automatically injected request contexts and shared utilities for time handling, structured logging, measurement, and experimentation.</p><p>Reaching across domain boundaries is intentionally more difficult. This comes with an upfront cost, including additional boilerplate, a learning curve, and stricter review. We accepted that cost because a shortcut today can become a multi-day investigation later.</p><h3>Enforcing it in CI, not just in docs</h3><p>Making the right path easier helped us introduce the framework. Keeping its boundaries intact required more than documentation and code review. We treated these rules as what Neal Ford and Mark Richards call <strong><em>“architectural fitness functions”</em></strong> in <a href="https://www.amazon.com/dp/1492086894">Software Architecture: The Hard Parts</a>. By codifying our layer boundaries into an <a href="https://www.archunit.org/">ArchUnit</a> test suite, our CI pipeline serves as an automated guardrail, catching structural drift on every pull request before a human reviewer ever sees it.</p><p>For example, model classes cannot access any other layer. A repository’s internal models, clients, and proxies contain the raw structures it uses to communicate with a database or an external API. Only that repository can access them. A service or handler cannot reach past the repository boundary and use them directly.</p><p>These are the same rules described in our design documentation, but instead of relying on reviewers to catch each violation manually, our CI pipeline runs them as executable tests on every build. One of those architecture checks looks like this:</p><pre>s”$moduleName’s Repositories layer” should {<br> “not expose internal model classes” in<br> layers<br> .optionalLayer(“RepositoryModels”)<br> .definedBy(s”com.agoda.bapi.$modulePackage.repository.model..”)<br> .whereLayer(“RepositoryModels”)<br> .mayOnlyBeAccessedByLayers(“Repositories”)<br> .check(importedClasses)<br>}</pre><p>This changes the review contract. Reviewers no longer need to detect every forbidden dependency manually because CI rejects those violations before the merge request reaches them.</p><p>The checks are a hard gate. New violations fail the build rather than being absorbed into the baseline. Framework-backed domains are evaluated against these rules, while legacy areas that have not yet migrated remain outside their scope.</p><h3>What the numbers actually say</h3><p>Once the framework was in wider use, we wanted to know whether it was actually making development easier. We started by looking at merge-request data.</p><p>We focused our analysis on the first half of 2026, when the framework was mature, and adoption was broad enough for a meaningful comparison. We excluded the earlier rollout period because the framework was still evolving and the samples were small. We track three metrics:</p><ul><li><strong>Review Time to First Feedback (RTTF):</strong> The time from opening an MR to receiving the first substantive review response, measured in hours.</li><li><strong>Code Review Finish Time (CTRF): </strong>The time required to complete review after it begins, measured in hours.</li><li><strong>Merge Lead Time Complete (MLTC):</strong> The total time from the first commit to merge, measured in days</li></ul><h3>Head-to-head: framework-backed vs. core legacy code</h3><p>We compared two mutually exclusive cohorts:</p><ul><li><strong>Platform at 90% or above:</strong> MRs with at least 90% of their changed lines in framework-backed domains.</li><li><strong>Legacy at 90% or above:</strong> MRs with at least 90% of their changed lines in the core legacy code, where most production changes outside the Platform framework are made.</li></ul><p>We compared the Platform cohort with the busiest part of the legacy codebase rather than grouping all non-framework code together. This gave us a more meaningful view of how the two approaches performed under active development.</p><p>MRs that did not reach 90% in either direction were excluded from this comparison because they represent a different question: the cost of work that crosses architectural boundaries.</p><p>Within each monthly cohort, we also removed the smallest and largest 10% of MRs based on lines changed. We applied the size filter separately because Platform and Legacy MRs differed greatly in typical size. A single cutoff for both groups would have removed too many MRs from one cohort while having little effect on the other.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9PFpulgkfXVMlxdluevI-g.png" /><figcaption><em>Figure 2: Month-over-month median and average for RTTF, CTRF, and MLTC — Platform ≥90% (blue) vs Legacy ≥90% (orange). Through Q1 (Jan–Mar) the two lines are tangled together; in Q2 (Apr–Jun) the Legacy lines climb while Platform stays flat; July-August continues the divergence, with Platform’s MLTC advantage widest in August (2.72 vs 5.02 days median).</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SqXttQ15EqnKWkLlOWd4PA.png" /><figcaption><em>Sample Size Table: “% BAPI MRs” is the share of that month’s Platform ≥90% + Legacy ≥90% MRs that survive the p10–p90 trim.</em></figcaption></figure><p>During the first quarter, neither cohort showed a consistent advantage. CTRF medians changed position from month to month, and RTTF was similar between the groups.</p><p><strong>Then, in Q2, the lines separate. </strong>Legacy review times increased while the Platform results remained comparatively stable. <strong>Averaged</strong> over Q2 2026, framework-backed MRs completed code review <strong>64% faster</strong> (CTRF median 1.08 vs 3.02 hrs) and received first feedback <strong>34% sooner</strong> (RTTF median 10.34 vs 15.77 hrs).</p><p>This occurred even though the framework-backed MRs were generally larger.</p><p>MLTC varies for several reasons, including implementation time, scheduling, and release timing. Review is only one part of that measure, but it is one part that the codebase can directly influence.</p><p>When code is easier to place and review, those gains can help shorten the overall path to merge.</p><h3>Why did review speed improve?</h3><p>Our interpretation is that the first-to-second-quarter change reflects a familiarity curve. Before the framework, placement often depended on reviewer judgment. Reviewers had to ask where logic belonged and whether a change touched the correct files.</p><p>The framework reduced that ambiguity by giving every module a repeatable structure. As reviewers became familiar with it, conversations shifted away from code placement and toward business logic, edge cases, and failure modes.</p><p>The framework itself did not change between the two quarters. What changed was how familiar reviewers became with its conventions. We interpret this directionally, as the monthly sample sizes remain modest, with 4 to 21 Platform MRs and 14 to 32 Legacy MRs.</p><p>In a migrated module, a change stays within a small vertical slice. Instead of tracing the entire call graph, a reviewer can focus on that slice. Three design choices help make this possible:</p><ul><li><strong>Explicit roles.</strong> Services decide, repositories translate, and models are built through builders. The file’s role signals to the reviewer what to expect, so placement questions become less common over time.</li><li><strong>Explicit wiring.</strong> Startup declares the request flow in one place instead of hiding it behind implicit resolution and cross-package indirection.</li><li><strong>Executable boundaries.</strong> ArchUnit turns invalid cross-layer shortcuts into build failures before a reviewer has to find them manually.</li><li><strong>Encapsulated complexity: </strong>Each layer and its interactions are engineered to reduce the cognitive load on the code readers rather than the writers. This directly mirrors John K. <a href="https://web.stanford.edu/~ouster/cgi-bin/aposd.php">Ousterhout’s observation</a>: <em>“Complexity is more apparent to readers than writers”</em>.</li></ul><p>A migrated startup class looks like this:</p><pre>lazy val acceptChangeSetupMiddlewareChain: MiddlewareChain[AcceptChangeSetupRequest, AcceptChangeSetupResponse] =<br> MiddlewareBuilder(middlewareInitializer)<br> .use(validateAcceptChangeSetupRequest)<br> .use(fetchAcceptChangeBookingDetails)<br> .use(orchestrateAcceptChangeSetup)<br> .build()</pre><p>That one chain answers the two questions that usually dominate review time in a legacy system: “where does this get called?” and “what else can this change touch?” Once both are cheap, cycle time tightens across the whole distribution.</p><p>The same boundedness changes the shape of risky migrations. When one framework-backed domain widened a numeric identifier from Int to Long, the expand-contract migration split into reviewable phases because the repository carried the transitional mapping logic while the domain model remained stable; in legacy code, the same change tends to collapse into a single cross-cutting PR. That’s the practical version of <a href="https://medium.com/agoda-engineering/mesh-programming-the-art-of-parallel-development-c71f74126be0">mesh programming</a>: the architecture doesn’t just make code cleaner, it makes risky work decomposable.</p><h3>Why did code contribution speed improve? (MLTC)</h3><p>Review speed is only part of the equation; the rest is writing and landing the change. To probe whether the framework’s boundaries made code easier to reason about before review, we looked at one of the first platform modules to reach critical mass, `<strong>cancellation</strong>`, and ran static analysis on the place where its logic used to reside in the legacy codebase (`<strong>server</strong>`).</p><p>At first glance, comparing a global legacy module to a single domain component might not seem like a fair, like-for-like comparison. But from a contributor’s perspective, this is exactly where the value lies. Previously, a developer working on a cancellation feature had to navigate and commit to `<strong>server</strong>`, exposing them to the cognitive load and blast radius of an all-encompassing monolith. Today, that same developer’s cognitive footprint is restricted entirely to the isolated `<strong>cancellation</strong>` module.</p><p>The structural difference between these two working environments (without test files) is stark:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/1*0xgVWNRd-xWuB7LFuOejbQ.png" /></figure><p>We also ran the cyclomatic-complexity check enforced by our CI, which uses a threshold of 10 branches per method.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a0wxHEKR8w-xDhKf9_Ccng.png" /></figure><p>AmendmentService.scala alone accounts for 7 of the server’s 136 cyclomatic complexity violations. The cancellation module had none across all 202 files. This provides a code-shaped explanation for the review results. Before the review begins, a framework-backed change generally asks the author and the reviewer to understand a smaller, flatter surface.</p><h3>It’s not just humans anymore</h3><p>When we designed the framework, we did not set out to measure its effect on AI-assisted development. But by 2026, AI coding agents were involved in a meaningful share of BAPI development, and the question became hard to ignore: Does an architecture built for human comprehension also help a machine?</p><p>The most striking signal is not cost or loops. It is adoption. From <strong>June through August 2026</strong>, <strong>91%</strong> of Platform ≥90% MRs had recorded AI activity, against <strong>69%</strong> for Legacy ≥90% MRs. We read this as engineers voluntarily reaching for agents more often on platform code — the place where an agent has the best chance of being useful.</p><p>Why would that be? An AI agent faces a challenge similar to that of a reviewer. It has to build a sufficient understanding of the codebase to decide what can be changed safely. If clear boundaries shrink a person&#39;s search space, they should also shrink the context and iteration an agent needs. When the payoff is higher, people use the tool more.</p><p>To explore this, we joined our filtered production-code MR dataset with our internal AI usage data, keyed to the GitLab MR identifier. We have reliably been tracking companion analysis from June through August 2026. That leaves us three months to work with, so the numbers below should be read directionally. We recorded per MR prompt activity and applied the same monthly, cohort-specific line-count trimming used in the review analysis.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5szjoz4bz49nMqk-pdDZXw.png" /></figure><p>Two things stand out once you look past adoption.</p><p><strong>First, the per-MR prompt counts are nearly identical </strong>— 20 median prompts on platform code versus 22 on legacy. On a raw, per-MR basis, an agent iterates about as much in either world. That is worth stating plainly, because it means the platform advantage is not about the agent “getting it right the first time.”</p><p><strong>Second, the advantage appears once you normalize by size.</strong> Platform MRs’ <strong>15.54 total prompts </strong>per 100 lines of changed code against legacy MRs’ <strong>21.80</strong> — the platform cohort is roughly <strong>29% cheaper</strong> per 100 LOC. The average-prompt-per-100-LOC figure points out that the platform cohort spends fewer iterations per unit of change. Though we treat these numbers as directional, a <strong>29% reduction in AI prompts</strong> is not a coincidence.</p><h4><strong>Why are AI agents cheaper on platform code?</strong></h4><p>The mechanism we proposed for human reviewers seems to apply, largely unchanged, to agents. In each case, the win is a smaller search space.</p><ul><li><strong>Predictable directory structure.</strong> In a layered module, an agent can locate the handler, service, repository, and model for a feature by convention rather than by exploration. Fewer files opened, fewer tokens spent orienting.</li><li><strong>The layer contract bounds the blast radius.</strong> Strict boundaries tell the agent and the CI that checks it where a change is allowed to reach. The agent does not have to reason about whether editing a repository quietly changes handler behavior three hops away, because the architecture forbids that path.</li><li><strong>Builders and generated scaffolding remove guesswork.</strong> Where the framework provides builders and code generation, the agent fills in a known shape rather than inventing one, which cuts the trial-and-error loops that dominate costs.</li><li><strong>Deterministic CI gives fast, unambiguous feedback.</strong> Arch-test guardrails and consistent test structure mean a wrong turn fails quickly and legibly, so the agent corrects on cheap signal rather than burning loops on a vague error.</li></ul><p>None of this makes the agent iterate fewer times per MR, recall the near-identical median loops. It makes each unit of change carry less surrounding context, so the same iteration count buys more verified code. On legacy modules, where boundaries are softer and structure varies from module to module, the agent spends more per line establishing what is safe to touch, and it seems engineers notice and reach for it less.</p><p>We did not design the framework for machines. But the property that helps a reviewer, a codebase you can reason about locally, turns out to be exactly the property that makes an agent both cheaper and more attractive to use. The question we’ll keep watching, as our data window grows, is whether the gap between the two cohorts holds.</p><h3>Conclusion</h3><p>Clear architecture pays off by moving the question of “where does this code belong?” out of every individual change and into the framework itself. Handlers orchestrate, services decide, repositories translate. The structure tells humans and AI agents where the code lives before they start changing it.</p><p>That is what the Platform framework brought us. We did not rewrite the system; we built a new skeleton alongside it and let it take on more weight over time. The older code is still there, but the new work now has clearer boundaries to follow.</p><p>However, adopting a new framework isn’t a one-time victory; it requires continuous improvement and eternal vigilance. Codebases naturally drift toward entropy, and the moment we stop defending these boundaries, the old habits of tight coupling will slide back in. We must constantly audit, refine, and enforce these rules as the system evolves.</p><p>If you’re facing a similar coupling problem, our takeaway is this: intentional friction at the boundary is worth it, but don’t take it on faith. Instrument your pull requests. Prove it to yourself. We did, and while our early metrics are directional, they are highly encouraging. Our strategic decision to invest in long-term design is already starting to pay dividends at the six-month mark. And if <a href="https://web.stanford.edu/~ouster/cgi-bin/aposd.php">John K. Ousterhout</a> is right that the true payback period of good software design is 6–18 months, we expect to see this positive trend solidify and settle over the next year.</p><figure><a href="https://careersatagoda.com/department/technology/?utm_source=medium&amp;utm_medium=blog&amp;utm_campaign=fasil&amp;utm_term=postbooking&amp;utm_content=Oct2026&amp;gh_src=971d88071us"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2156a3175538" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/how-we-cut-the-coupling-tax-in-a-post-booking-api-2156a3175538">How We Cut the Coupling Tax in a Post-Booking API</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Key Takeaways from Agoda’s AI Developer Report 2026]]></title>
            <link>https://medium.com/agoda-engineering/key-takeaways-from-agodas-ai-developer-report-2026-82915b88c370?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/82915b88c370</guid>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Tue, 29 Sep 2026 00:48:17 GMT</pubDate>
            <atom:updated>2026-09-29T00:48:17.035Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yYFBo1i7yrAY5ko8lBwhvg.png" /></figure><p><strong>Download the Full Report: </strong><a href="https://ago-da.co/4yXRa9T">https://ago-da.co/4yXRa9T</a></p><p>A year ago, we launched our first AI Developer Report to understand how AI was reshaping engineering work across Southeast Asia and India. This year, agentic AI is the story. Developers are moving from using AI to assist individual tasks to delegating entire workflows to systems that can plan, act, and complete meaningful parts of the work on their own. We surveyed developers and engineering leaders across India, Indonesia, Malaysia, the Philippines, Singapore, Thailand, and Vietnam to see how far that shift has gone.</p><p>The report presents a grounded view of how AI is reshaping software development across the region. This article summarizes the key findings and their implications for engineering teams and leaders.</p><h4><strong>Performance strengthened while trust held steady</strong></h4><p>AI is doing more, and developers still check it just as closely. More than 55% now save at least 7 hours a week, up from 18% a year ago, and most say AI-generated code is usable with only minor changes. The caution held. 79% still worry about hallucinations or incorrect output, and 86% review AI output always or most of the time. Usage also spread across the lifecycle, with code review at 84%, architecture and design at 81%, and testing at 73%, each up sharply from 2025. AI now runs across the whole development process, not just code generation.</p><h4><strong>Agent adoption is outpacing readiness</strong></h4><p>Adoption has passed the halfway mark. 53% of developers report agents in production or broad use, and nearly all have started experimenting. Readiness has not kept up. Only 38% call their codebase mostly or fully ready for a fully autonomous agent. The findings show readiness is about more than clean code. It also depends on how agents connect to existing systems, what they can access, whether teams can control their cost, and whether engineers can catch incorrect output. The gap is widest at larger organizations, where agents reach broad use before the codebase is ready to support them.</p><h4><strong>Cost is the leading barrier, and it hides where few teams look</strong></h4><p>Developers name cost as the biggest barrier to wider agent adoption. 28% put it first, ahead of integration complexity and governance, and that rises to 33% among teams already running agents in production. The more revealing finding is where the cost sits. It rarely comes from the model call. Most of the spend goes into the design, review and validation work around it, and it stays hidden while teams track tokens and seats instead of the work that surrounds them. OpenAI is framing cost the same way. Derrick Choi, Head of Codex Applied AI, APAC, says the company is focused on “how much useful work each dollar of intelligence can deliver.” The report reaches the same conclusion. Measure cost per unit of finished, verified work, make usage visible by feature or team, and choose the model as a configuration decision from the start.</p><h4><strong>Autonomy expands while accountability stays human</strong></h4><p>Developers grant autonomy by risk. 43% allow full autonomy for documentation, but 79% require human approval before production deployment. When an agent causes a problem, 42% hold the individual developer responsible and only 2% blame the AI vendor. Governance is formalizing alongside this. Comprehensive AI guidelines rose from 23% of organizations in 2025 to 39% this year. As Harley Young, Head of High-Tech at Microsoft, puts it: “The winners won’t be whoever adopts agentic AI first, but whoever builds trust and accountability around it. The opportunity we all share is to treat AI adoption as an operating-model change, not just a productivity tool.”</p><h4><strong>Developer leverage is rising unevenly</strong></h4><p>The leverage AI gives each developer is not reaching everyone equally. 49% of junior developers believe agents will make their roles less secure, compared with 17% of CTOs and VPs of Engineering. At the same time, developers expect their value to move up the stack. They rank system architecture and design (65%), AI literacy (61%), and agent orchestration (51%) as the top future skills, while coding fundamentals appear in just 18%. Access to AI tools is no longer the differentiator. Judgment is, and junior engineers have had the least time to build it.</p><h3><strong>How companies across the region are implementing it</strong></h3><p>The survey patterns show up in how regional companies are putting agents to work, and each pairs a new capability with control.</p><p><strong>Agoda</strong> extended its AI work from code review to development. Building on CodeBuddy, which reviews merge requests, we built CodeMaster this year, a platform that hosts coding agents on pre-warmed virtual machines running Agoda’s own environment. Instead of building its own agent, CodeMaster connects to the tools engineers already trust, aiming to write 10% of merge requests this year.</p><p><strong>Omise</strong> moved from AI-assisted coding to a broader development capability, completing a major platform upgrade in weeks rather than months by using AI to assess impacts across its codebase and by building governed agent workflows that keep accountability human.</p><p><strong>DataXet</strong> built an automated monitoring system where agents pull from internal and client data, flag anomalies and trends, and turn the findings into an executive summary with prioritized recommendations for leadership. It also treats cost control as an engineering discipline, on the view that the real risk is not AI being expensive but AI being used carelessly.</p><p><strong>SCB 10X</strong> already runs services built almost entirely by agents, but scales trust carefully. After seeing agents make confident but incorrect claims, it now tests more autonomous behavior in shadow mode behind an operator-controlled gate.</p><p>Across all four, none removed human oversight. They built agents into their existing quality and cost controls rather than around them.</p><h3><strong>What this means for leaders</strong></h3><p><strong>Rethink how AI cost is measured.</strong> Four in five developers already work under usage limits, yet cost is still the biggest barrier to adoption. Companies that measure cost by finished, verified work will scale further than those relying on restrictions alone.</p><p><strong>Manage AI risk as it moves into production.</strong> Managing AI risk is now the top challenge for engineering leadership, named by 31% of respondents. As agents reach production, static policies fall behind. Leaders need repeatable ways to decide what agents can do, how their output is verified, and where human approval is required.</p><p><strong>Invest in the next generation.</strong> Junior developers are far more likely to feel less secure. Access to AI tools is no longer the differentiator. Mentorship is when experienced engineers help juniors build the judgment that AI cannot teach.</p><figure><a href="https://careersatagoda.com/vacancies/?team%5B%5D=Technology"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=82915b88c370" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/key-takeaways-from-agodas-ai-developer-report-2026-82915b88c370">Key Takeaways from Agoda’s AI Developer Report 2026</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Agoda is evolving engineering support with AI agents]]></title>
            <link>https://medium.com/agoda-engineering/how-agoda-is-evolving-engineering-support-with-ai-agents-c907a54e236e?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/c907a54e236e</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Tue, 22 Sep 2026 09:59:02 GMT</pubDate>
            <atom:updated>2026-09-22T09:59:02.409Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Fasil Marshooq</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TyCpcjDcg5WYtXE5YZyqxQ.png" /></figure><p>At Agoda, we use Slack to communicate and collaborate across teams. It is also one of the places engineers turn when they need help with an internal service or platform. The conversation may begin in Slack, but the information needed to resolve an issue often sits elsewhere.</p><p>Consider a message like this:</p><p><em>“My application is down. It cannot connect to the database. What should I check?”</em></p><p>Even a question that sounds straightforward can have several possible causes, from a recent deployment or cluster problem to an unavailable downstream service. To narrow them down, the engineer responding may need to inspect dashboards, traces, source code, tickets, documentation, and earlier Slack conversations.</p><p>Before the investigation can begin, the team often needs to gather basic context and determine where the request should go. We wanted to make that process easier while keeping it within the tools and workflows our engineers already used.</p><p>In this blog post, we share how we approached this challenge, how our engineering support workflow evolved, and what we learned along the way.</p><h3>OpsBot: bringing service desk workflows into Slack</h3><p>Our first step was OpsBot, a Slack-native service desk for engineering teams. It plays a role similar to Zendesk, but keeps the request, conversation, and handoff in Slack.</p><p>OpsBot gives teams a consistent way to receive requests, collect relevant details, route them, and create tickets when needed. Each team can configure the workflow for its own domain without asking engineers to move to another support tool.</p><p>The process starts when an engineer runs an OpsBot command in Slack. The channel’s YAML configuration selects the appropriate support flow and opens a form. After the engineer chooses a topic and provides the requested details, OpsBot routes the request to the responsible team and can run a Python automation when one is configured.</p><p>This made the process more consistent, but the forms still assumed that engineers knew which technical details mattered. During an outage, someone may have only an error message, a Grafana link, or a related Slack thread. They may not know which cluster, pod, dashboard, or downstream service is relevant.</p><p>The next challenge was to keep the structure OpsBot provided while allowing engineers to begin with the information they already had.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I2Yko1GzR2fBZAv2QeDoKA.png" /></figure><h3>Evolving OpsBot from forms to conversations</h3><p>Forms work well when requesters know what information to provide. When they do not, large language models give us another way to gather that context. With OpsBot AI, requesters can start by describing the problem in their own words and sharing whatever evidence they already have.</p><p>OpsBot AI uses the initial message, the surrounding Slack conversation, and shared links to understand the request. If any required information is missing, it asks a focused follow-up question. The forms, routing rules, ticketing, and escalation paths remain in place, but requesters no longer need to complete every field before asking for help.</p><p>The YAML configuration continues to define the support topic, required information, routing, ticket rules, and optional Python automations. A requester asks for help in natural language, and OpsBot AI interprets the request, gathers the required context, assigns the responsible team, and creates the ticket.</p><p>This changes where the work begins. Requesters can start with the problem they see, while the engineering team responding receives a request with more of the required context already in place.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*de2c3gONoL8kTDnSpExeHA.png" /></figure><h3>Adding domain agents to the support workflow</h3><p>Making it easier to ask for help solved only part of the problem. A request could now reach the right team with better information, but the engineer responding might still need to build the investigation from scratch.</p><p>This led us to add domain agents to the workflow. OpsBot AI sends the Slack conversation to the agent configured for that support flow. The agent follows the team’s investigation guidelines and uses approved tools to gather more context.</p><p>For a service-down question, the agent might inspect OpenTelemetry signals and traces, search for a related incident, consult documentation, or examine the relevant dependency path in source code. If it finds an ongoing incident affecting the service, it can point the requester to the relevant update. For a more complex case, it can give the on-call engineer a summary of what it checked, what it found, and where the investigation could continue.</p><p>The domain agent runs separately on Agoda’s Agents Platform. If the request still needs human support, OpsBot AI keeps the existing ticket and escalation paths available.</p><h3>Letting domain teams build their own agents</h3><p>Agoda already had an internal Agents Platform, which gave OpsBot AI a way to connect each support flow to a domain agent.</p><p>The platform allows teams to create and operate their own agents. They can choose a model, write domain instructions, add skills, connect approved tools and Model Context Protocol (MCP) servers, and expose the agent through an Open Responses-compatible API. The team that owns the domain also decides how its agent should work.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kQs1-4gKyssWOf9frV8I2g.png" /></figure><p>OpsBot AI connects to the Agents Platform, which hosts agents for areas such as BI, infrastructure, and DevOps. The platform combines their skills with approved MCP integrations.</p><p>A BI team understands its data models and recurring questions. An infrastructure team knows which operational signals matter and when to escalate. A platform team understands its dependencies and common failure modes. Each team can turn that experience into instructions and reusable investigation guidelines for its agent.</p><p>Depending on the domain, agents can use approved integrations with tools such as Sourcegraph, Atlassian, Grafana, Tempo, Pyroscope, Glean, and related-ticket search.</p><p>Skills do not contain the domain knowledge itself. They guide the agent through an investigation by explaining which sources to consult, which checks to run, and when to involve an engineer. Some teams have also built pipelines to keep these skills aligned with changes in their workflows.</p><p>The Agents Platform also traces each run. When OpsBot AI posts an answer in Slack, authorised users can follow the trace link and see which tools the agent used. This helps domain owners understand what the agent considered, what it missed, and what they could improve in the next version.</p><h3>BI Product: an early success story</h3><p>BI Product adopted OpsBot AI support agents across Marketing, Customer Engagement Group (CEG), and Supply. Each area has an agent configured with its own domain instructions, skills, tools, and support workflow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jTBYBx9cw0_CjO3hwNI-3Q.png" /></figure><p>Together, the three agents expose 134 domain skills: 50 for Marketing, 43 for CEG, and 41 for Supply. The skills guide recurring investigations and help the agents use sources such as Confluence, OpenMetadata, and data tools consistently.</p><p>Between 1 June and 24 August 2026, the agents participated in 402 of 556 actual support threads across BI Product’s four core support channels, giving them 72.3% coverage. Here, participation means that the agent posted at least one response in the thread, and coverage is the share of actual support threads in which this happened. Both requesters and supporters could rate a response. Of the 286 threads that received feedback, 241, or 84.3%, were rated very or partially useful.</p><p>In 87 support threads, the agent provided a direct answer, meaning it completed the investigation and resolved the request without human analysis. The supporter only needed to close the ticket.</p><p>Marketing also saw a change in the effort required for support. After the rollout, ticket volume increased by 19%, while average logged supporter time fell by 15%, from 45.3 minutes to 38.5 minutes. For longer investigations, p90 effort fell from 120 minutes to 60 minutes, while median effort remained at 20 minutes.</p><p>The improvement appeared in the long tail, where more involved investigations required less time from supporters. These are early observations from an ongoing rollout. They do not prove that the agents alone caused the change, and they do not mean that every request was automated.</p><h3>Lessons from the rollout</h3><h4>Turn tribal knowledge into shared knowledge</h4><p>Building support agents pushed teams to document knowledge that previously lived in people’s heads. This made many investigations easier to repeat and reduced the dependence on a few domain experts.</p><h4>Skills guide the agent</h4><p>A skill is a procedure, not a knowledge source. It tells the agent how to investigate and where to look. The knowledge remains in Confluence, GitLab, OpenMetadata, Agoda’s data platforms, and other sources.</p><p>Skills also guide routing. A policy question should use an approved document instead of searching through code and logs. Most teams maintain routing skills based on their domain.</p><p>The same BI Product team mentioned earlier also uses a pipeline that reviews recent negative feedback and proposes skill updates through an MR. A human reviews each change before it is merged.</p><h4>Invest in error analysis</h4><p>Understanding an agent requires time and good observability. Traces show the path it followed, the tools it called, and where the investigation failed. Without them, explaining a “not useful” response would be difficult.</p><p>Error analysis also exposed limitations around the agent. When Slack was unavailable, we introduced FeedMCP to monitor its status feed and those of similar SaaS services.</p><p>When a Tempo query returned a 12 MB response and exceeded the agent’s context window, we began experimenting with tool-side summarisation, code mode, and sub-agents.</p><p>Manual error analysis helps with individual failures, but it does not scale. A skill or model change can fix one investigation and break another. This led us to Agent Eval, where these lessons can become repeatable checks.</p><h3>Closing the loop with Agent Eval</h3><p>As teams began updating their agents, they faced another challenge: how could they tell whether a change improved the agent without causing a regression elsewhere? A new model, skill, or tool integration could improve one type of response while affecting another answer or investigation path.</p><p>To answer that question, we introduced Agent Eval, which is currently in beta. OpsBot AI sends successful support calls and their usefulness feedback to our data platform. Agent owners review those interactions and select real questions and answers for an evaluation dataset. The feedback helps with curation, but domain owners decide which cases belong in the dataset and what a good result looks like.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VSSG0pH3kzEzZSjdCiY9EQ.png" /></figure><p>In the live workflow, OpsBot AI calls a domain agent through the Agents Platform and returns its answer to Slack. The interaction and its usefulness label are stored in the data platform. Agent owners can then use this production data to curate the evaluation dataset.</p><p>Agent Eval replays the saved cases through the Agents Platform and records the answers and tool calls. For each case, teams define the behaviour they want to check. They might assess the answer, require a particular tool call, or verify selected tool arguments. Running the same dataset against the current and proposed versions makes it easier to see how a change affects the agent.</p><p>We are also working towards shadow evaluation. This sends the same live request to two agent versions in parallel. The current version answers the requester, while the candidate version is evaluated without its response being shown. Teams can then assess the candidate using real traffic before releasing it.</p><h3>Where we go next</h3><p>Making engineering support better with AI has taken years, and the work is not finished. Have we replaced the entire support system with AI? No. Have we made the system better for requesters and supporters? The results so far suggest that we have.</p><p>The next stage is about helping the system improve continuously. Agent owners are building pipelines to evaluate agent behaviour and curate skills, with people reviewing the changes before they reach production. This is how we move towards self-improving agent harnesses while keeping domain owners in control.</p><p>Human support will remain essential when a problem requires judgment, ownership, or deeper investigation. The aim is to reduce the routine work that needs a person, giving engineers more time for the problems that genuinely need them.</p><figure><a href="https://careersatagoda.com/department/technology/?utm_source=medium&amp;utm_medium=blog&amp;utm_campaign=fasil&amp;utm_term=aiagent&amp;utm_content=Sept2026&amp;gh_src=971d88071us"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c907a54e236e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/how-agoda-is-evolving-engineering-support-with-ai-agents-c907a54e236e">How Agoda is evolving engineering support with AI agents</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Agoda Scaled Its Price Cache with DragonflyDB]]></title>
            <link>https://medium.com/agoda-engineering/how-agoda-scaled-its-price-cache-with-dragonflydb-0828e478e84f?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/0828e478e84f</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[data]]></category>
            <category><![CDATA[engineering]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Wed, 19 Aug 2026 05:19:32 GMT</pubDate>
            <atom:updated>2026-08-25T10:50:15.522Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by </em><a href="https://www.linkedin.com/in/clarkson-chang?utm_source=share_via&amp;utm_content=profile&amp;utm_medium=member_ios"><em>Clarkson Chang</em></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pRkCArtj9bGREWTWXIPE_g.png" /></figure><p><strong>Introduction</strong></p><p>At Agoda, delivering real-time hotel rates to millions of customers relies on a responsive and scalable pricing architecture. At the center of this system is our Price Cache, which stores terabytes of volatile rate data fetched from our global network of suppliers. When customers search for hotels, our backend services retrieve rates from this cache. A cache hit provides an immediate response. A cache miss triggers an on-demand workflow that fetches the latest rates and integrates them back into the system.</p><p>For years, MSSQL served as the primary storage engine for this critical component. As Agoda and its supplier network grew, the cache had to support increasing data volumes and read/write throughput. This placed our existing SQL Server architecture near its operational limits and made further scaling increasingly complex and resource-intensive.</p><p>In this blog post, we share how we migrated our Price Cache from SQL Server to DragonflyDB. You will learn why we selected DragonflyDB, how we completed the migration with minimal disruption, and how we improved failover and maintenance after the rollout.</p><h3>Challenges in scaling MSSQL for our use case</h3><p>Our legacy MSSQL setup relied on 72 shards, spread across multiple physical servers. The routing logic is maintained at the application level, and a drawback of this setup was its rigidity. To distribute the load evenly, we could not add a single server whenever we needed more capacity. Instead, we had to scale in predefined steps, such as moving from 6 to 12 or from 12 to 18 servers.</p><p>Each scale-out operation required our database team to remap the shards manually and migrate data to match the new topology. In early 2024, surging read and write volumes led us to double our hardware capacity. Within a year, we were approaching the limits again.</p><p>It became clear that continuing to add resources to SQL Server was not a viable or cost-effective long-term strategy.</p><h3>Searching for a successor: DragonflyDB</h3><p>Replacing SQL Server required more than selecting a fast database. Before the migration, our Price Cache served approximately 300,000 reads per second and close to 1.5 million writes per second, with strict P99 latency requirements of under 50 milliseconds.</p><p>The price dataset occupied approximately 1.5 TB of memory and continued to grow. Because the Price Cache is a tier 1 production system, we also needed high availability and the ability to scale without disrupting customer traffic.</p><p>DragonflyDB, a modern in-memory data store, stood out as a strong fit for our needs, particularly in the following areas:</p><ul><li><strong>Multi-threaded for high throughput: </strong>DragonflyDB uses a shared-nothing, multi-threaded architecture that partitions the dataset into smaller shards, each managed by a dedicated thread. This design enables parallel processing with minimal locking, helping DragonflyDB deliver high throughput at low latency.</li><li><strong>Redis compatibility and seamless scaling:</strong> DragonflyDB implements the Redis interface, allowing applications to use existing Redis clients. This suited our workload, which relies heavily on MGET and SET operations. DragonflyDB can also operate in Redis Cluster mode. When we need more cache capacity, we can add nodes to the cluster and redistribute slots to meet changing business requirements. This directly addresses the rigid scaling model we experienced with SQL Server.</li><li><strong>Built-in Expiry: </strong>Hotel rates are highly volatile and change frequently. With SQL Server, we had to build and maintain a cleanup job to remove stale supplier data because the storage engine did not provide the expiry behavior we needed. DragonflyDB handles expiry by allowing us to set a time-to-live when writing data, eliminating the need to maintain a separate cleanup process.</li><li><strong>Proven in-house expertise: </strong>Other engineering teams at Agoda were already using DragonflyDB for systems such as our Feature Store and Microcache DBaaS. This internal operational experience, combined with engineers who understood how to manage DragonflyDB clusters, gave us confidence to adopt it for a tier-1 production system.</li></ul><h3>Verifying DragonflyDB’s performance at Agoda’s scale</h3><p>DragonflyDB published benchmarks showing up to 25× higher throughput than Redis for GET and SET operations at comparable tail latencies. For a tier 1 production system operating at Agoda’s scale, we needed to validate its performance against our own workload.</p><p>We created a load test using Redis’s memtier_benchmark. Based on production telemetry, we modeled a 1:6 read-to-write ratio, with an average of 10 keys per MGET operation.</p><p>To generate a realistic load of more than 100,000 requests per second, we provisioned 10 pods. Each pod ran 20 Redis clients, each client used 6 threads, and each thread handled approximately 140 requests per second.</p><p>Under the simulated production load, DragonflyDB delivered lower P99 latency for both read (MGET) and write (SET) operations than our SQL Server production baseline.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M7TJZBzmbd_OCy6jpaCcJw.png" /></figure><p>We also confirmed that DragonflyDB’s shared bandwidth usage would remain below the physical server limit of 25 gigabits per second.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sMwA_sFOXAC1PakfQHHG7g.png" /><figcaption><em>Figure 1: DragonflyDB’s network bandwidth usage</em></figcaption></figure><h3>Our migration strategy</h3><p>Migrating a critical, high-throughput system required careful orchestration to protect customer experience and maintain business continuity. We divided the migration into four phases:</p><ol><li><strong>Planning and preparation:</strong> Provision the infrastructure, the application, and populate DragonflyDB with data.</li><li><strong>Parity checking:</strong> Read from both data stores and compare DragonflyDB with the existing SQL Server source, while continuing to serve customer traffic from SQL Server.</li><li><strong>Gradual rollout: </strong>Move read traffic from SQL Server to DragonflyDB through controlled experiments.</li><li><strong>Maintenance and optimization: </strong>Improve maintenance workflows and introduce automatic failover behavior.</li></ol><h4>Existing architecture</h4><p>Before the migration, our SQL Server Price Cache used an A/B setup for resilience. During data retrieval, the Price API performed round-robin reads across the SQL Server instances to distribute traffic between the two clusters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*S2yQcQHWnxzriUiszGgfBA.png" /><figcaption><em>Figure 2: A simplified diagram of the high level flow</em></figcaption></figure><h4>Phase 1: Planning and preparation</h4><p>We began the migration with a conservative design, then adjusted it as the dataset grew and our scaling requirements became clearer.</p><p>Our first deployment was a standalone DragonflyDB instance with 1 TB of memory. It stored only hot data, defined as itineraries with a length of stay between one and four days. We also introduced a new Price Writer and a gRPC gateway that provided an interface to the data layer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3roLOn_IuOW7P-4KjjpAoQ.png" /><figcaption><em>Figure 3: Newly introduced components for writing “hot” data</em></figcaption></figure><p>Within a few months, organic growth pushed this hot dataset to approximately 900 GB. Reaching our 90% memory-safety threshold triggered cache evictions to prevent out-of-memory failures. These evictions risked preventing customers from seeing the best available prices for their hotel searches.</p><p>To create sufficient long-term headroom, we moved to a sharded design with three shards per cluster.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KUFtbRoql3-MOXcNg7zkKA.png" /><figcaption><em>Figure 4: DragonflyDB in cluster mode</em></figcaption></figure><p>The additional capacity gave us an opportunity to store the complete price dataset in DragonflyDB. At that point, the dataset required approximately 1.5 TB of storage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zPA_pVaFqeoMO1p0ZhX55g.png" /></figure><p>After deploying the cluster, we began writing all price data to DragonflyDB. It processed approximately 1.6 million writes per second with a P99 latency of around 10 milliseconds.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AOBjQW7VJRRYN6oLc1RWvA.png" /><figcaption><em>Figure 5: Write throughput and latency</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MSDvZDmkse8_FXVZ7tRphw.png" /><figcaption><em>Figure 5B: Write throughput and latency</em></figcaption></figure><p>With data flowing reliably into DragonflyDB, the next step was to validate its accuracy.</p><h4>Phase 2: Parity checking</h4><p>Our Price API retrieves data using search criteria such as hotel, occupancy, check-in date, and check-out date. We implemented a dual-read mechanism in the Price API. SQL Server continued to serve customer requests, while the API asynchronously fetched the corresponding DragonflyDB data for comparison.</p><p>We evaluated parity across two dimensions:</p><ul><li><strong>Supplier count: </strong>Did both data stores return the same number of suppliers for the search criteria?</li><li><strong>Price-data length: </strong>For each supplier, did both data stores return price data of the same length?</li></ul><p>Comparing the full contents exactly would have been computationally expensive, so we used data length as a proxy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xFFGHgF0FcU1yPgAEE2rsQ.png" /><figcaption><em>Figure 6: How we checked for data parity across 2 data sources</em></figcaption></figure><p>We emitted the results as Prometheus metrics. Both dimensions achieved a match rate above 99.9%. Our investigation found that the remaining differences were primarily caused by benign variations such as timestamps and unique IDs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*39CRbYIbDEBWu_eJMSUQOQ.png" /><figcaption><em>Figure 7: Price Parity Results</em></figcaption></figure><p>This phase also allowed us to validate MGET performance under real production read traffic. The results aligned with our earlier benchmark. DragonflyDB delivered a P99 read latency of approximately 8 milliseconds while serving around 300,000 requests per second.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SaRyKulfdVAqTrlKXAlOoQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZNM3iry5vk6jaOEoaY281A.png" /><figcaption><em>Figure 8: Read Throughput and Latency</em></figcaption></figure><h4>Phase 3: Execution and Zero-Downtime rollout</h4><p>After validating data integrity and performance, we began serving customer requests from DragonflyDB. We used an A/B experiment to control the rollout. Customers in variant A continued to receive data from SQL Server, while customers in variant B received data from DragonflyDB.</p><p>Because DragonflyDB had already handled production-level read volumes during parity testing, we did not expect performance problems. The experiment focused on confirming that the transition would not negatively affect booking metrics. The results were positive. Within a few weeks, we moved 100% of customer traffic to variant B.</p><p>Once DragonflyDB was serving all price data, we deprecated the SQL Server read and write paths. This freed database resources for other use cases.</p><h4>Phase 4: Maintenance support and optimizations</h4><p>Our DragonflyDB architecture uses two clusters, A and B, for high availability. This allows our infrastructure team to perform routine maintenance, such as operating system patching and dependency upgrades, on one cluster without interrupting business operations.</p><p>The initial maintenance workflow involved several manual steps:</p><ol><li><strong>Traffic Rerouting:</strong> Mark cluster B as unreadable and send all read traffic to cluster A.</li><li><strong>Process Shutdown: </strong>Stop DragonflyDB on cluster B. Its health status becomes unhealthy, which stops writes to that cluster.</li><li><strong>Maintenance and Restart:</strong> Perform and complete the maintenance, then restart DragonflyDB. When the cluster becomes healthy, writes resume and begin repopulating the cache. Reads remain disabled because serving traffic from an empty cache could result in customers receiving no prices for their searches.</li><li><strong>Cache Warming:</strong> Monitor the cache-hit ratio. When it approaches the level of cluster A, mark cluster B as readable and resume sending read traffic to it.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pXofT3QaRTPHOzlAHeoKCA.png" /><figcaption><em>Figure 9: Cache Hit Ratio tracking during maintenance of the B-cluster</em></figcaption></figure><p>In the example above, cluster B required approximately three hours to reach the cache-hit ratio of cluster A, which was serving production traffic. The process required manual monitoring and consumed almost half a working day. We wanted to reduce both the manual effort and the recovery time.</p><p>The workflow also addressed only planned maintenance. During an unplanned outage, the absence of automatic failover meant that DragonflyDB could not continue serving prices until the issue was mitigated.</p><h4>Teaching the system to make failover decisions</h4><p>We wanted each application pod to detect an unhealthy cluster and automatically reroute traffic, without manual intervention.</p><p>The cache-hit ratio was the natural signal. A healthy cluster should have a hit ratio close to that of its counterpart. If one cluster’s ratio diverges significantly, it may indicate a problem.</p><p>The challenge is that cache-hit ratios fluctuate naturally as traffic patterns change. Rerouting traffic whenever the ratios differed would create frequent false alarms. Each pod, therefore, needed to distinguish a meaningful divergence from normal variation. Every five seconds, each pod compares the hit ratios of clusters A and B using the previous five minutes of traffic. It calculates how unlikely the observed gap would be if both clusters were healthy.</p><p>If the gap is too large to be explained by normal fluctuations, the pod treats it as a real problem and initiates failover. If the gap falls within the expected range, the pod continues serving traffic from both clusters.</p><p>Each pod performs this test independently using only the read and cache-hit counts it observes locally. Pods do not share their observations, and no coordinator or leader makes the decision for the fleet. This gives the failover mechanism a shared-nothing design in which every pod reaches its own conclusion about cluster health.</p><p>We used two different thresholds, depending on which direction we’re checking:</p><ul><li><strong><em>Going unhealthy</em> (healthy-threshold = 10%):</strong> if a cluster’s hit ratio falls at least 10 percentage points below the other’s, and the gap is statistically unlikely to be noise, the pod marks that cluster “unreadable” and stops sending reads to it.</li><li><strong><em>Recovering</em> (recovery-threshold = 3%): </strong>When the difference returns to within 3 percentage points and is no longer statistically significant, the pod marks the cluster as readable and resumes sending traffic to it.</li></ul><p>With this setup, we simulated an unplanned outage by intentionally bringing down one of the Dragonfly clusters. All application pods in the data center shown below entered the failover state within approximately two minutes. Around 40 pods were running in that data center at the time, and the process required no manual intervention.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bL4HcjLxTMc5xr7-aHT0Og.png" /><figcaption><em>Figure 10: Pods reaching consensus that B-cluster is in maintenance</em></figcaption></figure><p>The system is not perfect yet. We continue to tune the thresholds so that all pods reach the same conclusion more quickly. We are also exploring ways to shorten recovery by restoring an empty cluster from a snapshot of the healthy cluster instead of warming it from a cold cache.</p><p>Even with this remaining work, automated detection and rerouting represent a significant improvement over manually managed failover.</p><h3>Conclusion</h3><p>Since completing the migration from SQL Server to DragonflyDB, we have seen meaningful benefits. We improved P99 read latency by eight times while serving substantial traffic volumes. We also removed the rigid 72-shard SQL Server topology and retired the custom job that purged stale supplier data. Soon after the migration, growing business demand required us to increase storage capacity again. This time, we scaled without the operational complexity of the previous SQL Server architecture.</p><p>While our engineering teams continue to tune the failover thresholds and explore faster cluster-recovery methods, DragonflyDB has already proven to be a suitable foundation for the continued growth of Agoda’s pricing platform and global supply network.</p><h3>Acknowledgements</h3><p>This migration was a joint effort across several Agoda teams. We would especially like to recognize Charlene Chan, Yifan Huang, Dmitrii Sokolov, and Sachin Parthasarathy from the Supply Platform team. They led the migration and coordinated the work across the engineering teams involved.</p><p>We would also like to thank everyone who supported the migration:</p><ul><li><strong>Initiative sponsors:</strong> Prashant Singh, Ziv Ben Zuk, Preutsaji Lertsathienchai, and Tsahi Hakikat</li><li><strong>Database team:</strong> Rinson John, Wichai Atchariyabunyong, Avika Sangpenprow, and Thanarat Narapiromsuk</li><li><strong>Supply Tech team:</strong> Kanadech Jirapongtanavech</li><li><strong>Price API team:</strong> Johan Tiesinga</li></ul><figure><a href="https://careersatagoda.com/department/technology/?utm_source=medium&amp;utm_medium=blog&amp;utm_campaign=clarkson&amp;utm_term=dragonfly&amp;utm_content=Aug2026&amp;gh_src=971d88071us"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0828e478e84f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/how-agoda-scaled-its-price-cache-with-dragonflydb-0828e478e84f">How Agoda Scaled Its Price Cache with DragonflyDB</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From AI Assistant to Autonomous AI: The Next Chapter of AI in Software Engineering]]></title>
            <link>https://medium.com/agoda-engineering/from-assistant-to-autonomous-ai-the-next-chapter-of-ai-in-software-engineering-20b912b9a849?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/20b912b9a849</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Wed, 15 Jul 2026 09:00:25 GMT</pubDate>
            <atom:updated>2026-07-15T09:00:59.982Z</atom:updated>
            <content:encoded><![CDATA[<h4>Agoda’s AI Developer Report is back for 2026, and we want to know what you think</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FvBFbnwN_7C8MXdFdxuSCw.png" /></figure><p>Last year, we launched the first edition of the Agoda AI Developer Report, a research publication exploring how software developers across Southeast Asia and India are working with AI. The report surfaced insights from developers and tech leaders across the region on the tools they use, the challenges they face, and where they see AI heading. You can read the key takeaways here: <a href="https://medium.com/agoda-engineering/key-takeaways-from-agodas-ai-developer-report-2025-96d85276dcc2">Agodas AI developer report</a></p><p>We’re back with the second edition, and this year we’re going deeper.</p><figure><a href="https://macrameconsulting.typeform.com/agoda-ai-2026"><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0d5fykTLG4XhbIBAU_tTKQ.png" /></a></figure><h4><strong>This year’s focus: Agentic AI</strong></h4><p>AI is no longer just answering questions or suggesting lines of code. It is planning, building, and completing multi-step work autonomously. But how are developers actually experiencing this shift? What is working, what is not, and what does the future look like from where they sit?</p><p>These are the questions driving the 2026 Agoda AI Developer Report.</p><h4><strong>Who we’re surveying</strong></h4><p>We’re reaching out to software engineers, engineering managers, and tech leaders across Southeast Asia and India. Whether you’re already working with AI agents day-to-day or just starting to explore what agentic AI means for your team, your perspective matters.</p><h4><strong>How to participate</strong></h4><p>The survey takes around 15 to 20 minutes to complete. All responses are anonymous, and the findings will be published as a free external report later this year.</p><p>Take the survey here: <a href="https://macrameconsulting.typeform.com/agoda-ai-2026">AI Developer Report Survey 2026</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=20b912b9a849" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/from-assistant-to-autonomous-ai-the-next-chapter-of-ai-in-software-engineering-20b912b9a849">From AI Assistant to Autonomous AI: The Next Chapter of AI in Software Engineering</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How We Eliminated Configuration-Related Outages and Scaled Consul Management at Agoda]]></title>
            <link>https://medium.com/agoda-engineering/how-we-eliminated-configuration-related-outages-and-scaled-consul-management-at-agoda-a6f22199ab98?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/a6f22199ab98</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Thu, 09 Jul 2026 03:30:23 GMT</pubDate>
            <atom:updated>2026-07-09T03:45:44.751Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Supavet Amornruksakul</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V8wQxGSn35D01kz-rIHVCQ.png" /></figure><p>At Agoda, configuration management is mission-critical. We have thousands of microservices powering our travel platform across four global data centers, and we need a robust way to manage service configurations, feature flags, and runtime parameters. For years, HashiCorp Consul has been our backbone for service discovery and distributed configuration management.</p><p>Consul’s key-value store became the central nervous system of our infrastructure, storing everything from database connection strings and API timeouts to feature-flag toggles and regional service endpoints. The scale was substantial: over 34,000 key-value pairs managed critical configurations, with approximately 100+ changes made daily across our production environment. Engineers quickly updated configurations through Consul’s intuitive web UI, and services picked up changes in real time via Consul’s watch mechanism.</p><p>It worked beautifully… until it didn’t.</p><h3>The Problem: When Configuration Changes Became Our Biggest Risk</h3><p>As we scaled to hundreds of services and thousands of KVs across our four data centers (Hong Kong, Singapore, Amsterdam, and Ashburn), the way we managed configuration needed to evolve alongside it.</p><p>Teams were making direct changes through the Consul UI: quick edits that worked well at a smaller scale, but introduced new challenges as we grew:</p><ul><li><strong>Limited validation</strong>: Consul only provides regex pattern validation, which is difficult to configure and maintain. Complex validations, such as checking object structure, ranges, or dependencies between values, were never enforced.</li><li><strong>No review process</strong>: Critical configuration changes bypassed team reviews, increasing risk.</li><li><strong>Disaster recovery gaps</strong>: Configurations lived only in Consul. If something went wrong, we had no source of truth to restore from.</li></ul><p>The breaking point came when a misconfigured KV caused a production incident that took hours to diagnose because we couldn’t trace when or who made the change.</p><p>We needed a better way, one that brought the discipline of code review to configuration management.</p><h3>Understanding Our Consul Architecture</h3><p>Before diving into the solution, it’s important to understand how we structure configurations in Consul across our global infrastructure.</p><h4>Global vs Local Scopes</h4><p>Consul’s KV store at Agoda is organized into two distinct scopes:</p><p><strong>Global Scope (</strong><strong>global/*):</strong></p><ul><li>Configurations that should be consistent across all data centers</li><li>Managed exclusively in the Hong Kong (HK) data center</li><li>Automatically replicated to Singapore, Amsterdam, and Ashburn via <a href="https://github.com/hashicorp/consul-replicate">consul-replicate</a></li><li>Perfect for: feature flags, API endpoints, shared service configurations, company-wide settings</li><li>Example: global/payments/api-endpoint → same value in all 4 data centers</li></ul><p><strong>Local Scope (</strong><strong>local/*):</strong></p><ul><li>Data center-specific configurations</li><li>Each data center maintains its own independent values</li><li>No replication between data centers</li><li>Perfect for: database connection strings, region-specific rate limits, local service endpoints</li><li>Example: local/payments/database-host → different value in HK vs SG</li></ul><p><strong>A note on scope:</strong> Not everything stored in Consul KV today necessarily belongs there; some configurations would be better served by other mechanisms (service discovery, application config files, etc.). Migrating those values to more appropriate systems is a separate effort. This post focuses on how we brought safety to the configurations that live in <em>Consul.</em></p><h4><strong>The Risk of Configuration Changes</strong></h4><p>This architecture came with tradeoffs worth understanding for both scopes:</p><p><strong>Global scope risks:</strong></p><ul><li><a href="https://github.com/hashicorp/consul-replicate">consul-replicate</a> doesn’t support gradual rollout, so a single change to a global/ key in HK would instantly propagate to all four production data centers simultaneously.</li><li>A typo in a global timeout value or an incorrect feature flag could impact production services across all regions at once.</li><li>Without validation or review, mistakes were not caught until services began failing across multiple data centers simultaneously.</li></ul><p><strong>Local scope risks:</strong></p><ul><li>While limited to a single data center, mistakes could still cause regional outages.</li><li>Teams had to manually update the same configuration across multiple data centers without automation.</li><li>No validation meant typos and incorrect values could slip into any data center.</li></ul><p>The combination of instant global replication, a lack of validation, and the absence of audit trails made configuration management increasingly risky as we scaled. The larger the blast radius, the higher the stakes and global changes carried the highest risk of all.</p><h3>The Solution: GitOps for Consul</h3><p>We built a GitOps-based solution consisting of two key components:</p><h4><strong>1. Consul KV sync Service</strong></h4><p>A dedicated service that acts as the synchronization engine between Git and Consul. It handles all the heavy lifting:</p><ul><li>Watch for changes in the Git repository</li><li>Performs schema validation against JSON Schemas</li><li>Calculates diffs between Git state and current Consul state</li><li>Applies changes (PUT/DELETE operations) to Consul servers across all data centers</li></ul><h4><strong>2. Consul KV Git Repository</strong></h4><p>A Git repository that mirrors Consul’s KV structure, providing a familiar interface similar to the Consul UI but with Git’s benefits:</p><ul><li>Directory structure matches Consul’s data center and scope organization (e.g., hk/global/, sg/local/).</li><li>Each file represents a KV entry, just like viewing keys in the Consul UI</li><li>Engineers edit configuration files directly, commit changes, and open merge requests</li><li>All the power of Git: version history, blame, diff, revert, and branch workflows</li></ul><p>Instead of clicking through the UI, teams now manage configurations through merge requests with validation, code reviews, and full audit trails. The repository becomes the single source of truth, while Consul KV Sync ensures Consul stays in sync.</p><h3>Architecture Overview</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-TSJyYjcYL8cJQIQusZ5OQ.png" /><figcaption><em>Figure 1: End-to-end workflow from merge request to production deployment</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TFFFyHi6d0aafmqpMaQZcQ.png" /><figcaption><em>Figure 2: Validation detail and feedback loop</em></figcaption></figure><p>The system orchestrates a complete GitOps workflow from code review to production deployment:</p><p><strong>Workflow Steps:</strong></p><ul><li>Engineer modifies the KV files in the Git repository and opens a merge request</li><li>CI pipeline validates changes against JSON Schemas and posts a diff report comment</li><li>CODEOWNERS review and approve the changes</li><li>MR merges to the main branch</li><li>GitLab webhook triggers the Consul KV Sync service</li><li>Consul KV Sync applies changes (PUT/DELETE) to the appropriate Consul data centers</li><li><a href="https://github.com/hashicorp/consul-replicate">consul-replicate</a> propagates global/ changes from HK to other DCs automatically</li></ul><h3>Repository Structure</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HvgaRp3UWIF_VbebcTIC_A.png" /><figcaption>Figure 2: Repository structure mirroring Consul’s data center and scope organization</figcaption></figure><h4><strong>Key design decisions:</strong></h4><ul><li><strong>Git as the single source of truth</strong>: The repository is the authoritative source for all configuration changes. All changes must go through Git. Consul UI writes are blocked for onboarded paths, preventing configuration drift.</li><li><strong>Repository structure mirrors Consul</strong>: The directory structure (hk/global/, sg/local/, etc.) directly maps to Consul’s data center and scope organization, making it intuitive for engineers.</li><li><strong>Validation enforcement</strong>: Every KV has a JSON Schema that validates values before they reach production, catching errors during review. Integrated with AI assistance to help detect potential issues as well.</li><li><strong>Approval enforcement</strong>: No configuration change reaches production without approval from designated owners. This ensures more eyes review changes before deployment, catching potential issues early.</li></ul><h3>The Migration Journey</h3><p>Migrating over 34,000 KVs manually from Consul to Git would have been impractical. Copying values, writing schemas, and configuring permissions for each team would have taken months of tedious work and been highly error-prone. Automation was the only viable path forward.</p><h4>How We Migrated</h4><p>We built a fully automated onboarding process that eliminated manual work entirely:</p><ul><li><strong>Auto-sync existing KVs from Consul to Git</strong>: All configurations were automatically pulled from Consul and committed to the Git repository, preserving the exact directory structure.</li><li><strong>Auto-generated validation schemas</strong>: JSON Schemas were automatically generated based on the existing KV values. Teams didn’t write any schemas manually.</li><li><strong>Auto-assigned team permissions</strong>: CODEOWNERS were automatically assigned based on previous path ownership, routing reviews to the right teams.</li><li><strong>Auto-revoked UI write access:</strong> Once a team was onboarded, their Consul UI write permissions were automatically revoked, ensuring all future edits went through the new Git-based workflow.</li></ul><p>Teams received a pre-built MR with their KVs, schemas, and permissions already configured. They simply reviewed for accuracy, approved, and immediately started using Git as their single source of truth. Zero manual migration work required.</p><h4><strong>The Results:</strong></h4><p>Over 4 months, 3 engineers completed the migration from ideation to full implementation:</p><ul><li>Over 34,000 KVs migrated from Consul UI to Git repository</li><li>100+ teams onboarded across all engineering organizations</li><li>4 data centers now fully managed through Git</li><li>100% adoption. All Consul KV configurations now managed via Git</li></ul><h3>Real-World Impact</h3><p>Beyond the migration numbers, the day-to-day operational impact has been just as significant:</p><ul><li><strong>100% audit trail</strong>: Every configuration change is traceable with author, timestamp, and reason.</li><li><strong>Zero configuration-related outages</strong>: After teams onboarded to the new process, schema validation catches errors before production. No configuration change has caused an outage.</li><li><strong>Faster incident resolution</strong>: Full version control history makes troubleshooting straightforward, and rollback is simple, just revert the MR to restore the previous working state.</li></ul><h3>What’s Next</h3><p>While the current system has eliminated configuration-related outages, we’re continuously improving:</p><ul><li><strong>Gradual rollout</strong>: Currently, global configuration changes propagate instantly to all four data centers. We’re introducing a phased deployment that allows changes to roll out datacenter by datacenter. This prevents cascading service instability: if an issue is detected in one data center, the rollout can be paused before it affects other data centers, minimizing outages and improving the customer experience.</li><li><strong>Distributed ownership</strong>: Currently, when teams need to update the CODEOWNERS file to change ownership of their KV paths, DevOps approval is required. We’re evolving this model to allow teams to manage their own CODEOWNERS entries directly. This removes DevOps as a bottleneck and gives teams full autonomy to manage their configuration ownership without waiting for external approvals.</li></ul><p>And more! We’re actively exploring additional enhancements, including automated drift detection, pre-deployment testing, and enhanced AI-powered change impact analysis to make configuration management even more robust and developer-friendly.</p><h3>Conclusion</h3><p>Moving from UI-based configuration management to GitOps wasn’t just a technical migration; it was a cultural shift in how we think about configuration. By treating configuration as code, we applied the same rigor, safety, and auditability we expect of application code.</p><p>The result is a more reliable, transparent, and scalable configuration management system that supports Agoda’s mission to serve millions of travelers worldwide.</p><figure><a href="https://careersatagoda.com/department/technology/?utm_source=medium&amp;utm_medium=blog&amp;utm_campaign=nakarin&amp;utm_term=config&amp;utm_content=July2026&amp;gh_src=971d88071us"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a6f22199ab98" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/how-we-eliminated-configuration-related-outages-and-scaled-consul-management-at-agoda-a6f22199ab98">How We Eliminated Configuration-Related Outages and Scaled Consul Management at Agoda</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agoda Launches Bug Bounty Program on HackerOne to Connect with Global Research Community]]></title>
            <link>https://medium.com/agoda-engineering/agoda-launches-bug-bounty-program-on-hackerone-to-connect-with-global-research-community-8e67530f0934?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/8e67530f0934</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Thu, 25 Jun 2026 06:46:52 GMT</pubDate>
            <atom:updated>2026-06-30T02:24:04.643Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6VTb7YNvbd-eBMbHmJ3vSw.png" /></figure><p>Last year, we shared a behind-the-scenes look at how Agoda runs its bug bounty program, the lessons learned, the challenges of striking the right balance, and where we were heading. Today, we’re announcing the next chapter: Agoda’s bug bounty program is now open to the public on HackerOne.</p><h4><strong>From Private to Public</strong></h4><p>Since 2016, Agoda has run a private bug bounty program, working with an invited community of ethical hackers to identify and address vulnerabilities. Going public means opening that collaboration to the broader global security research community, more perspectives, more expertise, and more opportunities to catch what internal teams and private programs might miss.</p><h4><strong>Why Bug Bounty Programs Matter</strong></h4><p>Every website has bugs, and some will have a security impact. While traditional security measures like code reviews, penetration testing, and automated tools are critical, they can’t account for everything. A bug bounty program helps channel the skills of ethical hackers toward finding and reporting vulnerabilities, rather than exploiting them.</p><p>The scale of the problem is worth keeping in mind. In mid-2024, IBM published its Cost of a Data Breach report, which put the average cost of a breach at $4.88 million USD. The 2024 Verizon Data Breach Investigations Report, drawing on data from 6,963 attacks, identified a 180% increase in cyberattacks originating from software vulnerability exploitations compared to 2023. For a platform like Agoda, handling the payment information, personal details, and booking data of millions of travelers, robust external collaboration is essential.</p><h4><strong>What’s in Scope</strong></h4><p>The public program covers Agoda’s core web services and APIs, including Agoda.com and our mobile application. Researchers can test for a range of vulnerabilities, with common findings and their likely severity ranges including:</p><ul><li>Authentication bypass (High to Critical)</li><li>SQL Injection (High to Critical)</li><li>Remote Code Execution (High to Critical)</li><li>Insecure Direct Object Reference (Medium to Critical)</li><li>Stored Cross-site Scripting (Low to High)</li><li>Multi-factor Authentication Bypass (Low to High)</li></ul><p>Rewards are structured across four severity tiers:</p><ul><li>Low: $150</li><li>Medium: $500 to $700</li><li>High: $2,000 to $3,000</li><li>Critical: $5,000 to $6,000</li></ul><p>Bounties are awarded based on demonstrated impact. Researchers should provide clear steps to reproduce, proof of concept, and a realistic assessment of the vulnerability’s potential impact on Agoda and its users.</p><h4><strong>How to Participate</strong></h4><p>Researchers should self-provision test accounts, include a unique HackerOne identifier in HTTP requests, and submit detailed reports that include steps to reproduce, a proof of concept, and demonstrated impact. We commit to a first response within 2 business days and triage within 2 business days, with bounty payment within 14 business days of triage.</p><p>Reports should include a vulnerability summary, prerequisites, steps to exploit it, proof of the exploit, and an impact statement. The more detail you provide upfront, the faster we can triage and respond.<br>Our response targets are:</p><ul><li>Time to first response: 2 business days</li><li>Time to triage: 2 business days</li><li>Time to bounty: 14 business days from triage</li></ul><p>Full program details, rules of engagement, and scope are available at <a href="https://hackerone.com/agoda-public">https://hackerone.com/agoda-public</a>. We look forward to working with the global security research community.</p><figure><a href="https://careersatagoda.com/department/technology/?utm_source=medium&amp;utm_medium=blog&amp;utm_campaign=agodeng&amp;utm_term=flightintegrations&amp;utm_content=June2026&amp;gh_src=971d88071us"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8e67530f0934" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/agoda-launches-bug-bounty-program-on-hackerone-to-connect-with-global-research-community-8e67530f0934">Agoda Launches Bug Bounty Program on HackerOne to Connect with Global Research Community</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Agoda Simulates Booking Flows to Test Flight Integrations]]></title>
            <link>https://medium.com/agoda-engineering/how-agoda-simulates-booking-flows-to-test-flight-integrations-204ec4f2e128?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/204ec4f2e128</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[data]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Fri, 29 May 2026 06:10:32 GMT</pubDate>
            <atom:updated>2026-05-29T06:10:31.991Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Attaphong Rattanaveerachanon</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kyFVeNC6_UZu6PhipJwLpg.png" /></figure><h3>Flights Connectivity Background</h3><p>At Agoda, our flight booking platform integrates with many external partners, including airlines, OTAs, and providers. Each partner speaks its own dialect, has different API contracts, constraints, and protocols. To manage this complexity, we built a connector layer that sits between Agoda’s platform and each supplier, translating communications, mapping requests and responses, and handling errors and retry logic.</p><p>Building and testing new supplier connectors has traditionally been a bottleneck. Our end-to-end testing approach required spinning up a full production-like stack. When bugs surfaced late in this process, fixing them meant rerunning the entire suite, adding significant time to delivery.</p><p>In this blog, we share how we built a Workflow Simulator that validates supplier connectors by simulating their surrounding ecosystem, enabling engineers to run realistic multi-step scenarios locally and catch integration issues earlier in the development cycle.</p><h3>What is the connector?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eplLYRLkc6JRhT1SlGVFbw.png" /><figcaption>Figure 1: High-Level Architecture</figcaption></figure><p>The connector is a critical layer in the architecture that sits between Agoda and suppliers, translating context across all business operations, including search, pricing, ancillaries, and booking, as well as post-booking flows. Because each supplier has its own variations, the connector is responsible for normalization. This includes field mapping, error handling, coordinating multiple API calls, and business logic to transform supplier-specific responses into Agoda’s context.</p><p>Since the connector facilitates the translation of all business operations, it requires extensive testing to ensure these operations work as expected and are normalized correctly. Incorrect mapping logic in the connector can propagate across multiple domains or expose customers to incorrect or incomplete information, leading to a poor user experience.</p><p>It is not enough to validate only individual business operations. We must ensure that the entire end-to-end (E2E) flow works correctly. Whenever a new Connector is built, E2E testing is mandatory to give us confidence that Agoda can interact successfully with the new supplier through the newly implemented connector, from itinerary search to successful booking and PNR issuance, as well as post-booking operations such as cancellation.</p><h3>The Challenge with Testing Supplier Connectors</h3><p>As described above, as a supplier connector is business critical, it usually requires substantial testing effort–including unit, functional, and E2E tests–to ensure the quality and reliability of the supplier integration.</p><p>For E2E testing, we typically run these tests only after the connector implementation is complete. This involves spinning up the full stack, mirroring the production architecture, and connecting it to the supplier’s test environment. The stack itself includes multiple components. These range from frontend services to asynchronous messaging systems, caching layers, multiple microservices, background workers, etc.</p><p>Once the E2E stack is ready, we execute multiple test scenarios starting from the UI and validate both the functional outcomes and the supplier request-response logs. The logs are then collected and used for test assertions.</p><p>This methodology slows development, increases costs, and often surfaces defects late in the delivery cycle. The tests themselves are highly supplier-specific with limited reusability. When bugs are discovered during E2E testing, they introduce higher project risk and significantly greater remediation cost. Moreover, after a fix is applied, we typically need to rerun the entire E2E test suite, which further increases effort and slows delivery.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ddPchU2RDoKpXPP-R4m-rg.png" /><figcaption>Figure 2: The later a bug is discovered, the higher the cost of fixing it.</figcaption></figure><p>Additionally, E2E tests can be blocked by issues in unrelated dependencies, such as platform issues or unstable upstream or downstream services, which can slow down the supplier integrations.</p><p>To address these challenges, we asked a simple question: <em>What if we could test connectors without spinning up the entire system around them?</em></p><h3>Introducing Workflow Simulator</h3><p>A more effective approach is to validate connectors by simulating their surrounding ecosystem: modeling the upstream callers, generating realistic multi-step requests for specific business scenarios, preserving state across business processes, and asserting behavior across the entire workflow.</p><p>For example, instead of relying on real upstream systems from the E2E environment, the simulator can execute a complete booking flow from searching to booking with minimal resources. This allows the test to run faster, automatically, without irrelevant dependencies or context.</p><p>Because this simulator is designed specifically for connector testing, it prioritizes simplicity and clarity of execution over scalability and availability. For instance, while production systems rely on caching and asynchronous communication to achieve scale, the simulator intentionally omits these optimizations. Instead, it performs synchronous calls and records all interactions in a straightforward and transparent manner, making execution easier to trace and failures easier to diagnose.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SXWWHOFlXlIT5XFginbWAA.png" /><figcaption><em>Figure 3: Workflow simulator substitutes the real-world architecture</em></figcaption></figure><p>The simulator builds realistic scenarios that reflect business logic in production, such as searched itineraries, selected pricing, provided passenger information, selected ancillaries, etc. It then simulates the same sequence of requests that upstreams would make. Across these calls, it maintains state so that carry-over data, such as supplier-specific data, selected seats, paid bags, etc., remains consistent throughout the flow.</p><p>Once the execution completes, the simulator runs assertions on the recorded request-response pairs at both the individual endpoint level and across multiple endpoints to ensure end-to-end connector correctness.</p><h3>Architecture</h3><p>At a high level, the Workflow Simulator replaces upstream systems with a lightweight, controllable testing environment. It consists of four main components that work together to simulate, execute, and validate connector behavior.</p><ol><li>Scenario Builder: defines test criteria and booking</li><li>Test Runner: runs a list of scenarios</li><li>Workflow Executor: simulates the complex architecture and traffic</li><li>Assertions: validates correctness</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hb46YlEDpL-vu4uOS_eZ1g.png" /><figcaption><em>Figure 4: Workflow Simulator Architecture</em></figcaption></figure><h3>Scenario Builder</h3><p>The Scenario Builder enables us to define target acceptance testing scenarios that reflect real business use cases.</p><p>For example:</p><ul><li>Domestic one-way flight: 2 adults, 1 child, 1 infant (economy class with paid baggage)</li><li>International multi-segment round-trip: 2 adults (business class)</li><li>Domestic one-way with technical stop: 1 adult (economy class with paid seat)</li></ul><p>This Scenario Builder instantiates the Scenario Context, which contains key scenario information such as passenger composition, itinerary details, cabin class, and selected ancillaries. These attributes are later used for both request generation and assertions, ensuring consistency across the entire workflow.</p><p>Some fields that would normally originate from upstream systems or user input are generated by the Scenario Builder using a strategy pattern. Depending on the objective, we can use:</p><ul><li>A deterministic strategy to keep values fixed and repeatable.</li><li>A randomization strategy to generate unique requests or identifiers for each execution.</li></ul><p>The latter is particularly useful for suppliers with stateful test environments, where repeated identifiers may be rejected or cached across runs.</p><h3>Workflow Executor</h3><p>The Workflow Executor is responsible for simulating the upstream behaviors and orchestrating the sequence of calls to a connector. This executor is supplier-agnostic because it interacts with a connector via internal contracts, so it can be used with any supplier, including new suppliers under development.</p><p>The workflow is modeled as a directed acyclic graph (DAG) that represents the simulated business steps. A DAG is a way to represent tasks in which each node has a clear direction, and no path loops back to an earlier node, meaning there are no circular dependencies. The main benefit of this representation is that it helps determine the sequence of execution, improving clarity and making dependencies easier to understand.</p><p>Complex interactions such as asynchronous operations, cache polling, and message passing are simplified to nodes and edges on the DAG below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jx8N1knZM-5rmmuo6Lrzfg.png" /><figcaption><em>Figure 5: All operations in the complex system are modeled and simulated as a DAG</em></figcaption></figure><p>Once the workflow DAG is initialized, the executor traverses the graph from the start node to the end node. As it moves through the workflow graph, it interacts with most nodes, then with the connector, and eventually with the suppliers. The executor records each request and response and maintains a shared state, carrying forward any required information from one step to the next.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EUQLkkRiUYtrmSN4vRmKXA.png" /><figcaption><em>Figure 6: The workflow executor traverses the DAG to execute each step in the correct order</em></figcaption></figure><p>After workflow execution terminates, either because an error occurs or because the final node is reached, the executor runs the subsequent assertions based on the recorded interactions and accumulated state.</p><h3>Assertions</h3><p>The simulator supports two types of assertions: endpoint assertions and workflow assertions.</p><h4>Endpoint assertion</h4><p>It validates a single connector endpoint’s request and response, including contract validation, required fields, field type, format, and length constraints, and logical consistency across fields.</p><p>For example, at the pricing endpoint, it validates that the flight number field is alphanumeric and of valid length, and that the total price matches the sum of per-passenger prices.</p><h4>Workflow-Scenario assertion</h4><p>It validates end-to-end consistency across multiple connector endpoints using the defined scenario context and recorded request–response logs. It ensures responses align with the intended scenario (e.g., passenger details, leg count for round-trip itineraries, segment counts for multi-segment itineraries), verifies cross-step data propagation, and compares supplier-side booking details against the recorded requests and the scenario, including itinerary attributes and passenger information.</p><p>For example, for paid baggage, the Ancillaries Pricing step returns available baggage options, the selected baggage is included in the booking request, and the same baggage selection is reflected when retrieving booking information from the supplier.</p><h4>Test Runner</h4><p>The test runner serves as the entry point for initializing the simulator and executing the connector under test. Even if the implementation is not yet complete, engineers can still use the simulator to automate interactions across multiple suppliers’ endpoints and validate behavior and mapping logic using endpoint-level assertions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fjlyk22pkAa9wi-5IJjnLw.png" /><figcaption><em>Figure 7: Introducing a workflow simulator into the development process to catch issues earlier</em></figcaption></figure><h4>Benefits</h4><p>This approach enables fast local testing by allowing engineers to run realistic flows on their machines without spinning up the entire E2E stack. It significantly shortens the feedback loop, making it easier to reproduce issues, iterate on fixes, and validate changes early in the development cycle.</p><p>It also supports scenario-aware assertions. Instead of only checking that a single endpoint returns a valid response, the framework validates that overall behavior matches the intended scenario.</p><p>Because the simulator communicates through the connector’s internal contract, it is supplier-agnostic and reusable. This simulator can be applied across multiple suppliers and connectors with minimal additional work.</p><p>This simulator enables shift-left testing, as engineers can run it on realistic flows early in implementation. It catches issues sooner and reduces the risk of late-cycle defects and downstream surprises.</p><h3>Limitations and Boundaries</h3><p>This approach is not a replacement for full end-to-end testing. The E2E test is required for final validation and integration to verify behavior against the production architecture, including asynchronous communication, messaging, and parallelism.</p><p>There are some complex failures that may not be applicable to the workflow, or we may need to support this in the future. For example, race conditions and production-only constraints, such as rate limiting, cannot be reproduced without a full staging environment.</p><p>Finally, if a supplier’s test environment is unstable, that instability will propagate to the workflow simulator, reducing its reliability and overall effectiveness.</p><h3>What’s next?</h3><p>Testing supplier connectors by simulating the world around them, driving realistic scenarios, maintaining state across calls, and asserting correctness at both endpoint and workflow levels gives teams faster feedback and earlier visibility into integration issues.</p><p>Looking ahead, we will evolve the simulator to support richer, more production-like scenarios and explore AI-assisted techniques to help generate scenarios and edge cases, improving test coverage and quality. We’ll also expose the simulator capability to suppliers so both sides can validate the integration earlier, with shorter feedback loops and lower communication overhead.</p><figure><a href="https://careersatagoda.com/department/technology/?utm_source=medium&amp;utm_medium=blog&amp;utm_campaign=Ohm&amp;utm_term=flightintegrations&amp;utm_content=May2026&amp;gh_src=971d88071us"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=204ec4f2e128" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/how-agoda-simulates-booking-flows-to-test-flight-integrations-204ec4f2e128">How Agoda Simulates Booking Flows to Test Flight Integrations</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Agoda Powers Multiple Travel Brands with One Design System]]></title>
            <link>https://medium.com/agoda-engineering/how-agoda-powers-multiple-travel-brands-with-one-design-system-a48de5f906af?source=rss----9bca683764b5---4</link>
            <guid isPermaLink="false">https://medium.com/p/a48de5f906af</guid>
            <category><![CDATA[design-systems]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Agoda Engineering]]></dc:creator>
            <pubDate>Wed, 13 May 2026 08:06:21 GMT</pubDate>
            <atom:updated>2026-05-14T08:26:46.214Z</atom:updated>
            <content:encoded><![CDATA[<p><em>by Samuli Lehtonen and Tinn Jittjana</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CZBevSXhO9IUfeC_ydhZFQ.png" /></figure><h3>Introduction</h3><p>When your white-label partner asks for a small color change, and it takes much more effort than it should, you know your design system does not scale. At Agoda, we work with multiple white-label travel partners, each with its own brand and visual identity. Our platform must adapt its appearance to match each partner’s brand identity, while still being powered by a single design system. Doing this in a scalable, easy-to-operate way has been a key challenge, especially as the number of partners has grown. In this post, you will learn how we power multiple brands from one design system, and how you can apply the same principles in your own work.</p><h3>What is white-labeling?</h3><p>At its core, white-labeling is the practice of creating a product or service that can be rebranded with a partner’s own name, logo, and customized visuals, while sharing the same underlying technology. In the context of design systems, white-labeling means building a set of UI components and styles that can be easily adapted to different brand identities without requiring code rewriting or duplication.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-XMKvO7euIdGyJLV7CwuZg.png" /><figcaption>Figure 1A: Theme A</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sL3KYvAffuFsz4zrz6IEnQ.png" /><figcaption>Figure 1B: Theme B</figcaption></figure><h3>Agoda Design System</h3><h4>Challenges of managing white-label styling</h4><p>One of the goals of white-label styling is to make global UI changes easy for us, while still allowing partners to customize their own look in a controlled way. Without a shared system, we end up achieving neither.</p><p>Each white-label partner typically has its own styling implementation. There is no single place to define shared styling decisions or tokens, and no clear boundary between what is shared and what is customizable. As a result, even minor visual changes must be applied manually to every partner.</p><p>For example, imagine changing a core UI token such as the standard button border radius or spacing values used across components. The intention is the same everywhere, but without a shared mechanism, the change has to be applied separately in each partner’s code. With many partners, this quickly becomes time‑consuming and error‑prone. Some partners may be missed, while others may implement the change differently.</p><p>This setup also makes it harder for partners to update their own values. Styling is often scattered across copied code rather than expressed as clear, overridable tokens. Small adjustments become risky, and it is not always obvious which values are safe to change without breaking layout or consistency.</p><h3>Design tokens in the Agoda design system</h3><p>Design tokens play an important role in Agoda’s design system. They are named, reusable values that store the visual decisions of a design system. We use design tokens instead of hard‑coding values to represent colors, spacing, font sizes, border radius, shadows, and more. For example, the `color.primary` token stores a brand’s primary color; one partner might use blue while another uses purple.</p><p>Another example is a token like `radius.base`, which controls the component border radius. This allows one brand to have rounded buttons while another can use squared buttons.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0UMzjBXf-hLuWR1sf1iSvw.png" /><figcaption>Figure 2: Design token for background colors</figcaption></figure><p>A design token is an identifier that maps to a concrete value, such as a color or a spacing value. You can see in Figure 2, for example, base.background that maps to a certain color. Changing these mappings allows the same identifiers to be used in the code while returning different real values across different white-label themes.</p><p>Design tokens are the mechanism that enables this customization. The goal is to allow flexibility while preserving the brand and product&#39;s core identity and cohesion, since changing a color token affects multiple areas. Now that we know what design tokens are, we can look at themes that use them.</p><h3>Themes in the Agoda design system</h3><p>In the Agoda design system, themes define how design tokens are used and overridden for a specific brand or context. Design tokens represent the core design decisions, such as colors, spacing, and typography. Themes provide a structured way for partners to replace selected token values with their own.</p><p>Themes provide partners with a structured interface for overriding tokens without modifying component code. For example, as shown in Figure 3A and Figure 3B, a partner can override the token used for the primary button color so it matches the color used elsewhere in their brand.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nmINfo6DedJKE9gw7Yy6ow.png" /><figcaption>Figure 3A: Agoda default theme</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LnAmTVTtH2edOKDfKLijPg.png" /><figcaption>Figure 3B: White-label theme with main difference in primary color and base border radius.</figcaption></figure><h3>Benefits of theming system</h3><p>The main benefits of theming are:</p><ul><li><strong>Scalability:</strong> Reuse the shared component logic across partners and swap token values to deliver a unique UI for each theme. New partner = new theme, not new code.</li><li><strong>Maintainability:</strong> Centralize all the design tokens in a single source of truth (e.g., colors, spacing, and typography). Developers can easily review and update all themes in one place.</li><li><strong>Efficiency:</strong> Onboard a new partner quickly by creating a new theme and overriding design tokens from the base theme.</li></ul><h3>White-labeling on a technical level</h3><p>Drone-js is Agoda’s shared library for building user interfaces. Our components are designed in Figma, built with React, and styled using ADS CSS utilities. This setup helps designers and developers work together efficiently and ensures our products look and feel consistent.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AeeYlKksoYhWcdtzpnMnsg.png" /><figcaption>Figure 4A: Architecture of Agoda Design System</figcaption></figure><p>Figure 4A describes the main architecture of the Agoda Design System. The designers design the components in Figma, and after rounds of reviews and feedback, the design is finalized, and the developers implement it.</p><p>The drone-js library is composed of multiple NPM packages, including:</p><p><strong>Core and base theme</strong></p><ul><li>@drone-js/core — provides the core components and other foundational parts.</li><li>@drone-js/theme — contains the base theme used across all brands.</li></ul><p><strong>White-label themes</strong></p><p>Brand-specific theme packages such as:</p><ul><li>@drone-js/theme-agoda</li><li>(and other white-label themes in their own @drone-js/theme-* packages)</li></ul><p>Each of these themes builds on @drone-js/theme by applying brand-specific overrides.*</p><p>Consumer apps import @drone-js/core and all required white-label themes. The correct white-label theme is applied based on the information received from the backend.</p><h3>The anatomy of a white-label theme</h3><p>Our white-label theme NPM packages, @drone-js/theme and brand-specific themes, such as @drone-js/theme-agoda, consist of two main parts: CSS files and JavaScript code. These packages are built using both the default theme JSON and a white-label theme JSON.</p><p>The default theme JSON defines the base values for all design tokens. The white-label theme JSON contains only the token overrides specific to a brand. During the package building process, the white-label theme JSON is deep-merged into the default theme JSON to ensure all tokens exist and that the override theme can only include the tokens it wants to override.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bYFF-OUfE3fPknluml_W3A.png" /><figcaption>Figure 4B: Package building process</figcaption></figure><p>Figure 4B shows a default theme JSON that provides base values for all design tokens, while a white-label theme JSON defines some overrides; these two files are deep-merged into a complete Theme JSON, then used to generate the final main.css and index.js file and bundle into the NPM package (Figure 4C theme-agoda package) that has all the necessary variables and overrides for the white-label theme.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/683/1*wcMZClwMB1ZwZlHaoWuetg.png" /><figcaption>Figure 4C: Content of Agoda white-label NPM package</figcaption></figure><p>When the theme is bundled into an NPM package for use, as shown in Figure 4C, only the CSS and JS files are included. The theme JSON file is not needed since it is only used to generate the CSS that will be applied. Essentially, the JSON is the source of truth, and everything that we need is generated from it. When JSON updates are made, the files are regenerated, and a new version of the theme is released.</p><h3>How theming works at runtime</h3><p>The user interface of our application is built using the same UI framework, @drone-js/core, across all partners. Our UI framework renders components with the same structure and core logic that control component behavior, regardless of the partner. At the same time, the framework picks up styling from the partner’s theme package at runtime, such as @drone-js/theme-agoda, allowing visual differences to be expressed entirely through CSS variables.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/980/1*KNsBs5FFPzMVqbsVrFyA1g.png" /><figcaption>Figure 5: Theming in action</figcaption></figure><p>Check out our primary button as an example, showing each button with a unique look, including different colors, spacing, and border radii. In the DOM, all of the buttons have the same structure and class names, which are rendered by our UI framework (@drone-js/core). The theming system, from the theme package, uses main.css to define the CSS properties and theme.css to provide the theme-specific CSS variable overrides, allowing visual differences for each button.</p><h3>How does the application pick up the theme?</h3><p>Our UI framework provides a context provider, the <strong>Design System Context Provider</strong>, which configures the framework&#39;s behavior, such as text direction (e.g., LTR or RTL). One of its responsibilities is the theming system: it receives the white-label theme and injects the corresponding CSS variable values. Each component then references these variables via design tokens, allowing its appearance to be rendered according to the selected theme.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*q-uLTBYSbvl4agL54SdZZA.png" /><figcaption>Figure 6A: Example of DesignSystemContextProvider usage</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/776/1*ap5n_LUPR821d4mBlyeumA.png" /><figcaption>Figure 6B: Result of the code snippets in Figure 6A</figcaption></figure><p>The Design System Context Provider wraps the entire application and is configured with the white-label theme. It injects all CSS variables for the given theme, allowing UI components placed as its children to automatically render with the appropriate styles.</p><h3>Theme testing tools</h3><p>We adopt and build multiple tools to test our theming system and preview how our theme is applied across all components.</p><h3>Storybook</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qvON_GHMqGn_VxqesjaX3w.png" /><figcaption>Figure 7A: Theme selector</figcaption></figure><p>We have customized our Storybook by adding a Theme selector in the toolbar. The switcher updates our Design System Context Provider at runtime, so you can toggle between white-label themes and see how the same component is rendered in different themes. Developers consuming the library can also view supported props and accessibility guidelines in Storybook.</p><h3>Design token visualization</h3><p>Design tokens play a major role in the theming system. They control how components look and give each partner its own visual identity. We built a dedicated Storybook page for browsing tokens like color, spacing, and typography. This page makes tokens easier to understand, verifies their correctness, and improves visibility for both designers and developers who work closely with design tokens.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z6rvcix_3o48u-eopKNvPw.png" /><figcaption>Figure 8: Custom implemented token visualization page in the Storybook</figcaption></figure><p>This page also works with the theme switcher, so we can see how the design tokens are displayed in each white-label theme.</p><h3>Design token live editor</h3><p>Our themes are stored as JSON, a human-readable format that is easy to understand and edit. However, updating token values can be unintuitive for non-technical contributors, such as designers, and prone to human error. Our <strong>Design Token Live Editor</strong> provides a web interface for editing design tokens and instantly previewing changes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-FDG5GzWtRh59F6zXUawRg.png" /><figcaption>Figure 9: UI of design token live editor</figcaption></figure><p>This tool bridges the gap between designers and developers. Designers are the primary users. After completing the token update and reviewing the changes, they can export a theme as JSON and hand it off to developers, who can then integrate the file directly into the codebase. It can also be useful for other stakeholders, such as product managers.</p><h3>Conclusion</h3><p>The biggest thing we got right was separating shared component logic from brand-specific styling. Design tokens are the contract between the two. That separation is what makes the system scale; adding a new partner means adding a new theme file, not touching component code.</p><p>Figure 10 shows the full picture, from design in Figma through component development and theming in drone-js, to theme switching in consumer applications.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tcWkpg9vzZmWS3J8iICFhA.png" /><figcaption>Figure 10: End-to-end flow of the design token to the UI result</figcaption></figure><p>In summary, Agoda’s white-labeling strategy demonstrates how a thoughtfully architected design system can power a diverse ecosystem of brands and make it easy to onboard new partners and make edits to existing ones.</p><p>For more on how we build our design system, check out these Agoda blogs:</p><ul><li><a href="https://medium.com/agoda-engineering/building-and-scaling-different-travel-websites-with-one-codebase-fc6f0202c2e1">Building and scaling different travel websites with one codebase</a></li><li><a href="https://medium.com/agoda-engineering/pixels-to-product-innovating-with-design-system-1062f9645a34">Pixels to Product: Innovating with Design System</a></li></ul><figure><a href="https://careersatagoda.com/department/technology/?utm_source=medium&amp;utm_medium=blog&amp;utm_campaign=Samuli&amp;utm_term=Designsystem&amp;utm_content=May2026&amp;gh_src=971d88071us"><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qKAAoep6HRc8EBXkqWLK5g.jpeg" /></a></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a48de5f906af" width="1" height="1" alt=""><hr><p><a href="https://medium.com/agoda-engineering/how-agoda-powers-multiple-travel-brands-with-one-design-system-a48de5f906af">How Agoda Powers Multiple Travel Brands with One Design System</a> was originally published in <a href="https://medium.com/agoda-engineering">Agoda Engineering &amp; Design</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>