{"id":22830,"date":"2025-08-26T09:00:52","date_gmt":"2025-08-26T16:00:52","guid":{"rendered":"https:\/\/engineering.fb.com\/?p=22830"},"modified":"2025-08-26T11:25:05","modified_gmt":"2025-08-26T18:25:05","slug":"enabling-kotlin-incremental-compilation-on-buck2","status":"publish","type":"post","link":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/","title":{"rendered":"Enabling Kotlin incremental compilation on Buck2"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">The Kotlin incremental compiler has been a true gem for developers chasing faster compilation since its introduction in build tools. Now, we\u2019re excited to bring its benefits to <\/span><a href=\"https:\/\/buck2.build\/\"><span style=\"font-weight: 400;\">Buck2<\/span><\/a><span style=\"font-weight: 400;\"> \u2013\u00a0 Meta\u2019s build system \u2013 to unlock even more speed and efficiency for Kotlin developers.<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Unlike a traditional compiler that recompiles an entire module every time, an incremental compiler focuses only on what was changed. This cuts down compilation time in a big way, especially when modules contain a large number of source files.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Buck2 promotes small modules as a key strategy for achieving fast build times. Our codebase followed that principle closely, and for a long time, it worked well. With only a handful of files in each module, and Buck2&#8217;s support for fast incremental builds and parallel execution, incremental compilation didn\u2019t seem like something we needed.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">But, let\u2019s be real: Codebases grow, teams change, and reality sometimes drifts away from the original plan. Over time, some modules started getting bigger \u2013 either from legacy or just organic growth. And while big modules were still the exception, they started having quite an impact on build times.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">So we gave the Kotlin incremental compiler a closer look \u2013 and we\u2019re glad we did. The results? Some <\/span><b>critical modules now build up to 3x faster<\/b><span style=\"font-weight: 400;\">. That\u2019s a big win for developer productivity and overall build happiness.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Curious about how we made it all work in Buck2? Keep reading. We\u2019ll <\/span><b>walk you through the steps we took to bring the Kotlin incremental compiler to life<\/b><span style=\"font-weight: 400;\"> in our Android toolchain.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 1: Integrating Kotlin\u2019s Build Tools API<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">As of Kotlin 2.2.0, the only guaranteed public contract to use the compiler is through the command-line interface (CLI). But since the CLI doesn\u2019t support incremental compilation (at least for now), it didn\u2019t meet our needs. Alternatively, we could integrate the Kotlin incremental compiler directly via the internal compiler\u2019s components \u2013 APIs that are technically accessible but not intended for public use. However, relying on them would\u2019ve made our toolchain fragile and likely to break with every Kotlin update since there\u2019s no guarantee of backward compatibility. That didn\u2019t seem like the right path either.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Then we came across the Build Tools API (<\/span><a href=\"https:\/\/github.com\/Kotlin\/KEEP\/issues\/421\"><span style=\"font-weight: 400;\">KEEP<\/span><\/a><span style=\"font-weight: 400;\">), introduced in Kotlin 1.9.20 as the official integration point for the compiler \u2013 including support for incremental compilation. Although the API was still marked as experimental, we decided to give it a try. We knew it would eventually stabilize, and saw it as a great opportunity to get in early, provide feedback, and help shape its direction. Compared to using internal components, it offered a far more sustainable and future-proof approach to integration.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">\u26a0\ufe0f Depending on kotlin-compiler? Watch out!<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">In the Java world, a <\/span><i><span style=\"font-weight: 400;\">shaded<\/span><\/i><span style=\"font-weight: 400;\"> library is a modified version of the library where the class and package names are changed. This process \u2013 called shading \u2013 is a handy way to avoid classpath conflicts, prevent version clashes between libraries, and keeps internal details from leaking out.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Here\u2019s quick example:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Unshaded (original) class: <\/span><span style=\"font-weight: 400; color: #339966; font-family: 'courier new', courier;\">com.intellij.util.io.DataExternalizer<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Shaded class: <\/span><span style=\"font-weight: 400; color: #339966; font-family: 'courier new', courier;\">org.jetbrains.kotlin.com.intellij.util.io.DataExternalizer<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">The Build Tools API depends on the <\/span><i><span style=\"font-weight: 400;\">shaded <\/span><\/i><span style=\"font-weight: 400;\">version of the Kotlin compiler (kotlin-compiler-embeddable). But our Android toolchain was historically built with the <\/span><i><span style=\"font-weight: 400;\">unshaded <\/span><\/i><span style=\"font-weight: 400;\">one (kotlin-compiler). That mismatch led to <\/span><span style=\"font-weight: 400; font-family: 'courier new', courier; color: #339966;\">java.lang.NoClassDefFoundError<\/span><span style=\"font-weight: 400;\"> crashes when testing the integration because the shaded classes simply weren\u2019t on the classpath.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Replacing the unshaded compiler across the entire Android toolchain would\u2019ve been a big effort. So to keep moving forward, we went with a quick workaround: We unshaded the Build Tools API instead. \ud83d\ude48 Using the <\/span><a href=\"https:\/\/github.com\/google\/jarjar\"><span style=\"font-weight: 400;\">jarjar<\/span><\/a><span style=\"font-weight: 400;\"> library, we stripped the <\/span><span style=\"font-weight: 400; color: #339966; font-family: 'courier new', courier;\">org.jetbrains.kotlin<\/span><span style=\"font-weight: 400;\"> prefix from class names and rebuilt the library.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Don\u2019t worry, once we had a working prototype and confirmed everything behaved as expected, we circled back and did it right \u2013 fully migrating our toolchain to use the shaded Kotlin compiler. That brought us back in line with the API\u2019s expectations and gave us a more stable setup for the future.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 2: Keeping previous output around for the incremental compiler<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">To compile incrementally, the Kotlin compiler needs access to the output from the previous build. Simple enough, but Buck2 deletes that output by default before rebuilding a module.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">With <\/span><a href=\"https:\/\/buck2.build\/docs\/rule_authors\/incremental_actions\/\"><span style=\"font-weight: 400;\">incremental actions<\/span><\/a><span style=\"font-weight: 400;\">, you can configure Buck2 to skip the automatic cleanup of previous outputs. This gives your build actions access to everything from the last run. The tradeoff is that it\u2019s now up to you to figure out what\u2019s still useful and manually clean up the rest. It\u2019s a bit more work, but it\u2019s exactly what we needed to make incremental compilation possible.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 3: Making the incremental compiler cache relocatable<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">At first, this might not seem like a big deal. You\u2019re not planning to move your codebase around, so why worry about making the cache relocatable, right?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Well\u2026 that\u2019s until you realize you\u2019re no longer in a tiny team, and you\u2019re definitely not the only one building the project. Suddenly, it does matter.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Buck2 supports <\/span><a href=\"https:\/\/buck2.build\/docs\/users\/remote_execution\/\"><span style=\"font-weight: 400;\">distributed builds<\/span><\/a><span style=\"font-weight: 400;\">, which means your builds don&#8217;t have to run only on your local machine. They can be executed elsewhere, with the results sent back to you. And if your compiler cache isn\u2019t relocatable, this setup can quickly lead to trouble \u2013 from conflicting overloads to strange ambiguity errors caused by mismatched paths in cached data.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">So we made sure to configure the root project directory and the build directory explicitly in the incremental compilation settings. This keeps the compiler cache stable and reliable, no matter who runs the build or where it happens.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 4: Configuring the incremental compiler<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">In a nutshell, to decide what needs to be recompiled, the Kotlin incremental compiler looks for changes in two places:<\/span><span style=\"font-weight: 400;\"><br \/>\n<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Files within the module being rebuilt.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The module\u2019s dependencies.<\/span><\/li>\n<\/ul>\n<p><span style=\"font-weight: 400;\">Once the changes are found, the compiler figures out which files in the module are affected \u2013 whether by direct edits or through updated dependencies \u2013 and recompiles only those.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To get this process rolling, the compiler needs just a little nudge to understand how much work it really has to do.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">So let\u2019s give it that nudge!<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Tracking changes inside the module<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">When it comes to tracking changes, you&#8217;ve got two options: You can either let the compiler do its magic and detect changes automatically, or you can give it a hand by passing a list of modified files yourself. The first option is great if you don\u2019t know which files have changed or if you just want to get something working quickly (like we did during prototyping). However, if you&#8217;re on a Kotlin version earlier than 2.1.20, you have to provide this information yourself. Automatic source change detection via the Build Tools API isn\u2019t available prior to that. Even with newer versions, if the build tool already has the change list before compilation, it\u2019s still worth using it to optimize the process.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">This is where Buck\u2019s incremental actions come in handy again! Not only can we preserve the output from the previous run, but we also get hash digests for every action input. By comparing those hashes with the ones from the last build, we can generate a list of changed files. From there, we pass that list to the compiler to kick off incremental compilation right away &#8211; no need for the compiler to do any change detection on its own.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">Tracking changes in dependencies<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Sometimes it\u2019s not the module itself that changes, it\u2019s something the module depends on. In these cases, the compiler relies on classpath snapshot. These snapshots capture the Application Binary Interface (ABI) of a library. By comparing the current snapshots to the previous one, the compiler can detect changes in dependencies and figure out which files in your module are affected. This adds an extra layer of filtering on top of standard compilation avoidance.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">In Buck2, we added a dedicated action to generate classpath snapshots from library outputs. This artifact is then passed as an input to the consuming module, right alongside the library\u2019s compiled output. The best part? Since it\u2019s a separate action, it can be run remotely or be pulled from cache, so your machine doesn\u2019t have to do the heavy lifting of extracting ABI at this step.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-22835 aligncenter\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/classpath-snapshots-for-abi-4.png\" alt=\"\" width=\"506\" height=\"552\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/classpath-snapshots-for-abi-4.png 506w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/classpath-snapshots-for-abi-4.png?resize=96,105 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/classpath-snapshots-for-abi-4.png?resize=192,209 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<p><span style=\"font-weight: 400;\">If, after all, only your module changes but your dependencies do not, the API also lets you skip the snapshot comparison entirely if your build tool handles the dependency analysis on its own. Since we already had the necessary data from Buck2\u2019s incremental actions, adding this optimization was almost free.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 5: Making compiler plugins work with the incremental compiler<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">One of the biggest challenges we faced when integrating the incremental compiler was making it play nicely with our custom compiler plugins, many of which are important to our build optimization strategy. This step was necessary for unlocking the full performance benefits of incremental compilation, but it came with two major issues we needed to solve.<\/span><\/p>\n<h3><span style=\"font-weight: 400;\">\ud83d\udea8 Problem 1: Incomplete results<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">As we already know, the input to the incremental compiler does not have to include all Kotlin source files. Our plugins weren\u2019t designed for this and ended up producing incomplete results when run on just a subset of files. We had to make them incremental as well so they could handle partial inputs correctly.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-22836 aligncenter\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/incremental-compiler.png\" alt=\"\" width=\"775\" height=\"640\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/incremental-compiler.png 775w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/incremental-compiler.png?resize=768,634 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/incremental-compiler.png?resize=96,79 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/incremental-compiler.png?resize=192,159 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<h3><span style=\"font-weight: 400;\">\ud83d\udea8 Problem 2: Multiple rounds of Compilation<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The Kotlin incremental compiler doesn\u2019t just recompile the files that changed in a module. It may also need to recompile other files in the same module that are affected by those changes. Figuring out the exact set of affected files is tricky, especially when circular dependencies come into play. To handle this, the incremental compiler approximates the affected set by compiling in multiple rounds within a single build.<\/span><\/p>\n<p><i><span style=\"font-weight: 400;\">\ud83d\udca1Curious how that works under the hood? The <\/span><\/i><a href=\"https:\/\/blog.jetbrains.com\/kotlin\/2020\/09\/the-dark-secrets-of-fast-compilation-for-kotlin\/\"><i><span style=\"font-weight: 400;\">Kotlin blog on fast compilation<\/span><\/i><\/a><i><span style=\"font-weight: 400;\"> has a great deep dive that\u2019s worth checking out.<\/span><\/i><\/p>\n<p><span style=\"font-weight: 400;\">This behavior comes with a side effect, though. Since the compiler may run in multiple rounds with different sets of files, compiler plugins can also be triggered multiple times, each time with a different input. That can be problematic, as later plugin runs may override outputs produced by earlier ones. To avoid this, we updated our plugins to accumulate their results across rounds rather than replacing them.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-22837 aligncenter\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/multiple-rounds.png\" alt=\"\" width=\"1527\" height=\"726\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/multiple-rounds.png 1527w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/multiple-rounds.png?resize=916,436 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/multiple-rounds.png?resize=768,365 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/multiple-rounds.png?resize=1024,487 1024w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/multiple-rounds.png?resize=96,46 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/multiple-rounds.png?resize=192,91 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<h2><span style=\"font-weight: 400;\">Step 6: Verifying the functionality of annotation processors<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Most of our annotation processors use Kotlin Symbol Processing (KSP2), which made this step pretty smooth. KSP2 is designed as a standalone tool that uses the Kotlin Analysis API to analyze source code. Unlike compiler plugins, it runs independently from the standard compilation flow. Thanks to this setup, we were able to continue using KSP2 without any changes.<\/span><\/p>\n<p><i><span style=\"font-weight: 400;\">\ud83d\udca1 Bonus: KSP2 comes with its own built-in incremental processing support. It\u2019s fully self-contained and doesn\u2019t depend on the incremental compiler at all.\u00a0<\/span><\/i><\/p>\n<p><span style=\"font-weight: 400;\">Before we adopted KSP2 (or when we were using an older version of the Kotlin Annotation Processing Tool (KAPT), which operates as a plugin) our annotation processors ran in a separate step dedicated solely to annotation processing. That step ran before the main compilation and was always non-incremental.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 7: Enabling compilation against ABI<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">To maximize cache hits, Buck2 builds Android modules against the class ABI instead of the full JAR. For Kotlin targets, we use the jvm-abi-gen compiler plugin to generate class ABI during compilation.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">But once we turned on incremental compilation, a couple of new challenges popped up:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">The jvm-abi-gen plugin currently lacks direct support for incremental compilation, which ties back to the issues we mentioned earlier with compiler plugins.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">ABI extraction now happens twice &#8211; once during compilation via jvm-abi-gen, and again when the incremental compiler creates classpath snapshots.<\/span><\/li>\n<\/ol>\n<p><span style=\"font-weight: 400;\">In theory, both problems could be solved by switching to full JAR compilation and relying on classpath snapshots to maintain cache hits. While that could work in principle, it would mean giving up some of the build optimizations we&#8217;ve already got in place \u2013 a trade-off that needs careful evaluation before making any changes.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">For now, we\u2019ve implemented a custom (yet suboptimal) solution that merges the newly generated ABI with the previous result. It gets the job done, but we\u2019re still actively exploring better long-term alternatives.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Ideally, we\u2019d be able to reuse the information already collected for classpath snapshot or, even better, have this kind of support built directly into the Kotlin compiler. There\u2019s an open ticket for that: <\/span><a href=\"https:\/\/youtrack.jetbrains.com\/issue\/KT-62881\/Pass-to-the-compilation-only-ABI-snapshot-of-the-classpath\"><span style=\"font-weight: 400;\">KT-62881<\/span><\/a><span style=\"font-weight: 400;\">. Fingers crossed!<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Step 8: Testing<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Measuring the impact of build changes is not an easy task. Benchmarking is great for getting a sense of a feature\u2019s potential, but it doesn\u2019t always reflect how things perform in \u201cthe real world.\u201d Pre\/post testing can help with that, but it\u2019s tough to isolate the impact of a single change, especially when you\u2019re not the only one pushing code.\u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We set up A\/B testing to overcome these obstacles and measure the true impact of the Kotlin incremental compiler on Meta\u2019s codebase with high confidence. It took a bit of extra work to keep the cache healthy across variants, but it gave us a clean, isolated view of how much difference the incremental compiler really made at scale.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">We started with the largest modules \u2013\u00a0 the ones we already knew were slowing builds the most. Given their size and known impact, we expected to see benefits quickly. And sure enough, we did.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">The impact of incremental compilation\u00a0<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">The graph below shows early results on how enabling incremental compilation for selected targets impacts their local build times during incremental builds over a 4-week period. This includes not just compilation, but also annotation processing, and a few other optimisations we\u2019ve added along the way.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">With incremental compilation, we\u2019ve seen about a 30% improvement for the average developer. And for modules without annotation processing, the speed nearly doubled. That was more than enough to convince us that the incremental compiler is here to stay.\u00a0<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-22838 aligncenter\" src=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-modules.png\" alt=\"\" width=\"994\" height=\"612\" srcset=\"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-modules.png 994w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-modules.png?resize=916,564 916w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-modules.png?resize=768,473 768w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-modules.png?resize=96,59 96w, https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-modules.png?resize=192,118 192w\" sizes=\"auto, (max-width: 992px) 100vw, 62vw\" \/><\/p>\n<h2><span style=\"font-weight: 400;\">What\u2019s next<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Kotlin incremental compilation is now supported in Buck2, and we&#8217;re actively rolling it out across our codebase! For now, it\u2019s available for internal use only, but we\u2019re working on bringing it to the recently introduced <\/span><a href=\"https:\/\/github.com\/facebook\/buck2\"><span style=\"font-weight: 400;\">open source<\/span><\/a><span style=\"font-weight: 400;\"> toolchain as well.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">But that&#8217;s not all! We\u2019re also exploring ways to expand incrementality across the entire Android toolchain, including tools like Kosabi (the Kotlin counterpart to <\/span><a href=\"https:\/\/engineering.fb.com\/2017\/11\/09\/android\/rethinking-android-app-compilation-with-buck\/\"><span style=\"font-weight: 400;\">Jasabi<\/span><\/a><span style=\"font-weight: 400;\">), to deliver even faster build times and even better developer experience.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">To learn more about Meta Open Source, visit our <\/span><a href=\"https:\/\/opensource.fb.com\/\"><span style=\"font-weight: 400;\">open source site<\/span><\/a><span style=\"font-weight: 400;\">, subscribe to our <\/span><a href=\"https:\/\/www.youtube.com\/channel\/UCCQY962PmHabTjaHv2wJzfQ\"><span style=\"font-weight: 400;\">YouTube channel<\/span><\/a><span style=\"font-weight: 400;\">, or follow us on <\/span><a href=\"https:\/\/www.facebook.com\/MetaOpenSource\"><span style=\"font-weight: 400;\">Facebook<\/span><\/a><span style=\"font-weight: 400;\">, <\/span><a href=\"https:\/\/www.threads.net\/@metaopensource\"><span style=\"font-weight: 400;\">Threads<\/span><\/a><span style=\"font-weight: 400;\">, <\/span><a href=\"https:\/\/x.com\/MetaOpenSource\"><span style=\"font-weight: 400;\">X<\/span><\/a><span style=\"font-weight: 400;\"> and <\/span><a href=\"https:\/\/www.linkedin.com\/showcase\/meta-open-source?fbclid=IwZXh0bgNhZW0CMTEAAR2fEOJNb7zOi8rJeRvQry5sRxARpdL3OpS4sYLdC1_npkEy60gBS1ynXwQ_aem_mJUK6jEUApFTW75Emhtpqw\"><span style=\"font-weight: 400;\">LinkedIn<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Kotlin incremental compiler has been a true gem for developers chasing faster compilation since its introduction in build tools. Now, we\u2019re excited to bring its benefits to Buck2 \u2013\u00a0 Meta\u2019s build system \u2013 to unlock even more speed and efficiency for Kotlin developers. Unlike a traditional compiler that recompiles an entire module every time, [&#8230;]<\/p>\n<p><a class=\"btn btn-secondary understrap-read-more-link\" href=\"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/\">Read More&#8230;<\/a><\/p>\n","protected":false},"author":80,"featured_media":22834,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[174],"tags":[],"coauthors":[2243],"class_list":["post-22830","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-open-source","fb_content_type-article"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v19.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Enabling Kotlin incremental compilation on Buck2 - Engineering at Meta<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Iveta Kovalenko\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/\"},\"author\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#author\",\"name\":\"\"},\"headline\":\"Enabling Kotlin incremental compilation on Buck2\",\"datePublished\":\"2025-08-26T16:00:52+00:00\",\"dateModified\":\"2025-08-26T18:25:05+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/\"},\"wordCount\":2447,\"publisher\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2025\\\/08\\\/kotlin-thumb.png\",\"articleSection\":[\"Open Source\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/\",\"name\":\"Enabling Kotlin incremental compilation on Buck2 - Engineering at Meta\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2025\\\/08\\\/kotlin-thumb.png\",\"datePublished\":\"2025-08-26T16:00:52+00:00\",\"dateModified\":\"2025-08-26T18:25:05+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#primaryimage\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2025\\\/08\\\/kotlin-thumb.png\",\"contentUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2025\\\/08\\\/kotlin-thumb.png\",\"width\":1161,\"height\":607},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/2025\\\/08\\\/26\\\/open-source\\\/enabling-kotlin-incremental-compilation-on-buck2\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/engineering.fb.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Enabling Kotlin incremental compilation on Buck2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#website\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/\",\"name\":\"Engineering at Meta\",\"description\":\"Engineering at Meta Blog\",\"publisher\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/engineering.fb.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#organization\",\"name\":\"Meta\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/Meta_lockup_positive-primary_RGB.jpg\",\"contentUrl\":\"https:\\\/\\\/engineering.fb.com\\\/wp-content\\\/uploads\\\/2023\\\/08\\\/Meta_lockup_positive-primary_RGB.jpg\",\"width\":29011,\"height\":12501,\"caption\":\"Meta\"},\"image\":{\"@id\":\"https:\\\/\\\/engineering.fb.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/Engineering\\\/\",\"https:\\\/\\\/x.com\\\/fb_engineering\"]},[]]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Enabling Kotlin incremental compilation on Buck2 - Engineering at Meta","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/","twitter_misc":{"Written by":"Iveta Kovalenko","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#article","isPartOf":{"@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/"},"author":{"@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#author","name":""},"headline":"Enabling Kotlin incremental compilation on Buck2","datePublished":"2025-08-26T16:00:52+00:00","dateModified":"2025-08-26T18:25:05+00:00","mainEntityOfPage":{"@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/"},"wordCount":2447,"publisher":{"@id":"https:\/\/engineering.fb.com\/#organization"},"image":{"@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#primaryimage"},"thumbnailUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-thumb.png","articleSection":["Open Source"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/","url":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/","name":"Enabling Kotlin incremental compilation on Buck2 - Engineering at Meta","isPartOf":{"@id":"https:\/\/engineering.fb.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#primaryimage"},"image":{"@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#primaryimage"},"thumbnailUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-thumb.png","datePublished":"2025-08-26T16:00:52+00:00","dateModified":"2025-08-26T18:25:05+00:00","breadcrumb":{"@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#primaryimage","url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-thumb.png","contentUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-thumb.png","width":1161,"height":607},{"@type":"BreadcrumbList","@id":"https:\/\/engineering.fb.com\/2025\/08\/26\/open-source\/enabling-kotlin-incremental-compilation-on-buck2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/engineering.fb.com\/"},{"@type":"ListItem","position":2,"name":"Enabling Kotlin incremental compilation on Buck2"}]},{"@type":"WebSite","@id":"https:\/\/engineering.fb.com\/#website","url":"https:\/\/engineering.fb.com\/","name":"Engineering at Meta","description":"Engineering at Meta Blog","publisher":{"@id":"https:\/\/engineering.fb.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/engineering.fb.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/engineering.fb.com\/#organization","name":"Meta","url":"https:\/\/engineering.fb.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/engineering.fb.com\/#\/schema\/logo\/image\/","url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2023\/08\/Meta_lockup_positive-primary_RGB.jpg","contentUrl":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2023\/08\/Meta_lockup_positive-primary_RGB.jpg","width":29011,"height":12501,"caption":"Meta"},"image":{"@id":"https:\/\/engineering.fb.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Engineering\/","https:\/\/x.com\/fb_engineering"]},[]]}},"jetpack_featured_media_url":"https:\/\/engineering.fb.com\/wp-content\/uploads\/2025\/08\/kotlin-thumb.png","jetpack_shortlink":"https:\/\/wp.me\/pa0Lhq-5We","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/22830","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/users\/80"}],"replies":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/comments?post=22830"}],"version-history":[{"count":4,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/22830\/revisions"}],"predecessor-version":[{"id":22877,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/posts\/22830\/revisions\/22877"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/media\/22834"}],"wp:attachment":[{"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/media?parent=22830"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/categories?post=22830"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/tags?post=22830"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/engineering.fb.com\/wp-json\/wp\/v2\/coauthors?post=22830"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}