Posts Tagged ‘KotlinConf2024’
[KotlinConf2024] The Best Programmer I Know: Insights from KotlinConf2024
At KotlinConf2024, Daniel Terhorst-North shared a heartfelt reflection on the traits of exceptional programmers, drawing from his 30-year career and a colleague who embodies these qualities. Without a formal degree, this programmer excels by starting tasks, prioritizing outcomes, simplifying solutions, choosing tools wisely, and fostering team growth. Daniel’s narrative, blending personal anecdotes and practical advice, inspires developers to cultivate curiosity, resilience, and empathy while building impactful software.
Starting with Action
Great programmers dive into tasks without hesitation. Daniel recounted how his colleague tackles projects by starting anywhere, embracing the unknown. This “just start” mindset counters procrastination, which Daniel admits to masking as research. By iterating rapidly—trying, failing, and learning—programmers overcome perfectionism and ego. Daniel likened progress to navigating a grid city, moving stoplight to stoplight, accepting delays as part of the journey, ensuring steady advancement toward solutions.
Prioritizing Outcomes Over Code
Building products, not just code, defines effective programming. Daniel emphasized that emotional investment should focus on outcomes, not code, which is merely a means. The best programmers write minimal, high-quality code, holding no attachment to it. Studying the domain reveals user needs, as Daniel learned during a financial project where ignorance of CDOs led to unintended consequences. Observing users’ frustrations, like manual data entry, uncovers opportunities to eliminate friction, enhancing product value.
Simplifying the Complex
Exceptional programmers see through complexity to find simple solutions. Daniel shared a story of his colleague bypassing bloated Java web servers by writing a lean one from the HTTP spec. In another case, a team debating JSON libraries was guided to implement a simple interface for nine serialized objects, avoiding heavy dependencies. Writing clear documentation, like a streamlined README, drives “embarrassment-driven refactoring,” ensuring solutions remain concise and maintainable, solving only what’s necessary.
Choosing Tools for the Problem
Tool selection should prioritize the product, not team familiarity. Daniel recounted a team learning Scala to sketch code quickly, despite no prior experience, proving adaptability trumps comfort. He advocated for polyglot programming, using Advent of Code to learn Rust and Go, which broadened his problem-solving perspective. By minimizing cognitive distance between problem and solution, as Rich Hickey’s “Simple Made Easy” suggests, programmers select tools that evolve with project needs, ensuring flexibility.
Fostering Team Care
Great programmers uplift their teams. Daniel finds joy in pairing and teaching, inspired by an XKCD comic about the “lucky 10,000” who learn something new daily. He creates environments for learning, drawing from jiu-jitsu’s teaching-first philosophy. Sending teams home to rest, as Daniel advocates, boosts effectiveness, while assuming positive intent—per Virginia Satir’s family therapy principle—builds empathy, transforming conflicts into opportunities for collaboration and growth.
Building Psychological Safety
Psychological safety, per Amy Edmondson’s research, is vital for high-performing teams. Daniel explained that safe teams encourage saying “I don’t know,” seeking help, and disagreeing without fear. A study of surgical teams showed high performers report more errors, reflecting trust, not incompetence. In software, this translates to teams where questions spark learning, help fosters collaboration, and dissent drives improvement, creating dynamic, challenging environments that fuel innovation.
Growing as a Programmer
Personal growth sustains programming excellence. Daniel urged developers to stay current through communities, contribute actively, and remain skeptical of trends like AI hype. Practicing via challenges like Advent of Code sharpens skills, as Daniel found when switching languages mid-puzzle. Balancing work with physical activities, like running, and prioritizing rest prevents burnout. By embracing continual learning and kindness, programmers evolve, as Daniel’s colleague demonstrates, into impactful, resilient professionals.
Links:
[KotlinConf2024] Channels in Kotlin Coroutines: Unveiling the Redesign
At KotlinConf2024, Nikita Koval, a JetBrains concurrency expert, delved into Kotlin coroutine channels, a core communication primitive. Channels, often used indirectly via APIs like Flow, enable data transfer between coroutines. Nikita explored their redesigned implementation, which boosts performance and reduces memory usage. He detailed rendezvous and buffered channel semantics, underlying algorithms, and scalability, offering developers insights into optimizing concurrent applications and understanding coroutine internals.
Channels: The Hidden Backbone of Coroutines
Channels are fundamental to Kotlin coroutines, even if developers rarely use them directly. Nikita highlighted their role in high-level APIs like Flow. For instance, merging two Flows or collecting data across dispatchers relies on channels to transfer elements between coroutines. Channels also bridge reactive frameworks like RxJava, funneling data through a coroutine-launched channel. Unlike sequential Flows, channels enable concurrent data handling, making them essential for complex asynchronous tasks, as seen in Flow’s channelFlow
builder.
Rendezvous Channels: Synchronized Data Exchange
Rendezvous channels, the default in Kotlin, ensure synchronized communication. Nikita illustrated their semantics with two producers and one consumer. When a consumer calls receive
on an empty channel, it suspends until a producer sends data. Conversely, a producer’s send
suspends if no consumer is waiting, preventing uncontrolled growth. This “rendezvous” ensures direct data handoff, as demonstrated when a producer resumes a waiting consumer, maintaining efficiency and safety in concurrent scenarios.
Building Efficient Channel Algorithms
Implementing rendezvous channels requires balancing efficiency, memory use, and scalability. Nikita compared concurrent queue designs to adapt for channels. A lock-based sequential queue, while fast on single threads, fails to scale due to synchronization costs. Java’s ConcurrentLinkedQueue
, using a linked list, scales better but incurs high memory overhead—32 bytes per 4-byte reference. Instead, modern queue designs use a segmented array with atomic counters for enqueuing and dequeuing, minimizing memory and scaling effectively, forming the basis for channel implementation.
Buffered Channels: Flexible Data Buffering
Buffered channels extend rendezvous semantics by allowing a fixed-capacity buffer. Nikita explained that a channel with capacity k
accepts k
elements without suspension, suspending only when full. Using a single producer-consumer example, he showed how a producer fills an empty buffer, while a second producer suspends until the consumer extracts an element, freeing space. This design, implemented with an additional counter to track buffer boundaries, supports dynamic workloads, though cancellation semantics add complexity, detailed in JetBrains’ research papers.
Performance Gains from Redesign
The channel redesign significantly enhances performance. Nikita shared benchmarks comparing the old linked-list-based implementation to the new segmented array approach. In a multi-producer, multi-consumer test with 10,000 coroutines, the new channels scaled up to four times faster, producing less garbage. Even in sequential workloads, they were 20% quicker. Q&A revealed further tuning, like setting segment sizes to 32 elements, balancing memory and metadata overhead, ensuring scalability across 64-core systems without degradation.
Deepening Concurrency Knowledge
Understanding channel internals empowers developers to tackle performance issues, akin to knowing hash table mechanics. Nikita emphasized that while high-level APIs abstract complexity, low-level knowledge aids debugging. He invited attendees to explore further at the PDI conference in Copenhagen, where JetBrains will discuss coroutine algorithms, including schedulers and mutexes. The redesigned channels, applied to unlimited and conflated variants, offer robust, scalable communication, encouraging developers to leverage coroutines confidently in high-load applications.
Links:
[KotlinConf2024] Compose UI on a Light Switch: KotlinConf2024’s Embedded Adventure
At KotlinConf2024, Jake Wharton, a Kotlin enthusiast, shared his journey of running Compose UI on a smart light switch powered by embedded Linux. Sparked by a friend’s discovery of a backdoor granting root access, Jake transformed a $50 Amazon switch into a custom platform. He navigated challenges from setting up a JVM and Kotlin Native to configuring a graphics stack with Skia and handling touch inputs, culminating in a live demo of a touchable UI controlling the switch’s relay, showcasing Compose UI’s multiplatform potential.
A Side Project Ignited by a Backdoor
Jake’s project began in January 2023 when a friend revealed a smart switch’s ADB server allowed password-free root access, discovered via Home Assistant forums. The Decora-style switch, with a touchscreen, light sensor, and microphone, ran a stripped-down Linux. Jake snipped an extension cord to power it and confirmed root access via ADB, setting the stage for custom software. This accessibility, though later patched by the manufacturer, fueled his ambition to replace the Flutter-based UI with Compose UI, blending curiosity with technical challenge.
Bootstrapping the JVM and Kotlin Native
To run Compose UI, Jake first tested the JVM on the switch’s limited 150MB storage. He pushed a Linux ARM JRE and a Java “Hello World” class, confirming JVM functionality. Next, he compiled a Kotlin Native “Hello World” for Linux ARM, proving Kotlin could run natively. These steps established a foundation, but the switch’s constraints—no compiler, minimal storage—required compiling on a host machine and transferring binaries, a process complicated by the need to match the device’s library versions, like libDRM 2.4.
Wrestling with the Graphics Stack
Rendering Compose UI required a graphics stack, but the switch’s Flutter app used Linux’s Direct Rendering Manager (DRM) with OpenGL ES, not OpenGL. Jake’s initial attempt to use Compose UI Desktop failed due to missing OpenGL and X11 dependencies. He ported C-based DRM code to Kotlin Native, meticulously matching the switch’s libDRM version (2.4.87) by analyzing binaries. This process, spanning months, involved pulling headers and shared objects from the device, ensuring compatibility, and overcoming compilation hurdles, like missing toolchains for Linux ARM.
Skia: Rendering Pixels with Kotlin
To draw pixels, Jake turned to Skia, the graphics library powering Flutter and Android. JetBrains’ Skiko library, binding Skia to Kotlin, lacked Linux ARM support for OpenGL ES. Jake forked Skiko, modifying it to use EGL, and endured weeks of GitHub Actions builds to compile Skia for ARM. He integrated Skiko’s C++ and Kotlin bindings, enabling rendering of basic shapes (e.g., red backgrounds, blue circles). This step, though painful, proved Kotlin Native could handle the switch’s display, setting the stage for Compose UI.
Handling Touch Inputs
Interactivity required processing touchscreen inputs. Jake used Linux’s evtest
to identify the switch’s touchscreen events, capturing X/Y positions and touch states (down/up). He implemented a single-pointer event parser in Kotlin Native, mapping raw events to a data class within the render loop. This avoided multi-touch complexity, as the switch supported only one pointer. By feeding these events to Compose UI, Jake enabled touch interactions, like button ripples, transforming the switch into a responsive interface, despite occasional crashes during live demos.
Bringing Compose UI to Life
Integrating Compose UI was surprisingly straightforward compared to prior challenges. Jake forked JetBrains’ Compose UI repository, adding a Linux ARM target and implementing expect/actual declarations for dependencies like input handling and scrolling. He replaced low-level Skiko calls with Compose UI’s entry point, passing a Skia canvas and touch events. Initial text rendering crashed due to font issues, but a simple button worked, displaying ripples on touch. Jake’s UI, controlling the switch’s relay via a Unix domain socket and Ktor, demonstrated Compose UI’s adaptability, running the same code on desktop for development.
Demo and Future Potential
Jake’s live demo, despite power converter issues, showcased a draggable switch UI toggling a light bulb, built with Compose UI and Kotlin multiplatform. An enclosure housed the switch, highlighting its real-world application. He envisioned future enhancements, like Home Assistant integration or music control, noting the project’s infancy. Community contributions, like his friend Eric’s interactive component, and public forks for Skiko and Ktor, underscore the project’s collaborative spirit. Jake cautioned against internet-connecting the switch due to security concerns, urging manufacturers to simplify hacking for innovation.
Links:
[KotlinConf2024] Why We Can’t Have Nice Things in Kotlin: Library Challenges
Vsevolod Tolstopyatov, a Kotlin libraries team member at JetBrains, entertained KotlinConf2024 with a witty exploration of why Kotlin’s standard libraries evolve slowly. From Turkish locale bugs breaking compilers to time zone quirks skipping entire days, Vsevolod revealed the hidden complexities of API design. Through anecdotes like Twitter polls on naming and a friend’s missed alarm, he highlighted how human language, historical quirks, and real-world constraints challenge library development, urging developers to appreciate the slow, thoughtful process behind robust APIs.
Human Language: The Turkish I Debacle
Kotlin’s string functions, like toUpperCase
and toLowerCase
, rely on system locales, leading to unexpected issues. Vsevolod recounted a bug where code compiled in Turkey failed due to the Turkish dotted and dotless I. Uppercasing an English lowercase i
in Turkish yields a dotted uppercase İ
, breaking method names like intArray
. This forced the deprecation of locale-dependent functions, replaced with uppercase
and lowercase
using a root locale. The lesson: human languages’ diversity demands careful API design to avoid breaking code across cultures.
Naming Woes: The Capitalize Conundrum
Naming APIs is deceptively hard. Vsevolod shared how the deprecated capitalize
function lacked a clear replacement because “capitalize” means different things to different developers. Twitter polls revealed no consensus, and other languages like Python and Ruby use capitalize
inconsistently. With all good names taken or ambiguous, Kotlin’s team struggles to craft intuitive APIs without confusing users. This naming challenge slows library updates, as new functions risk misinterpretation or overlap with existing conventions.
Time Zones: Samoa’s Missing Day
Time zone complexities plague datetime APIs. Vsevolod described a case in Samoa, which skipped December 30, 2011, to align with Australia’s time zone, jumping from UTC-10 to UTC+14. A simple date addition (e.g., December 29 + 2 days) yields inconsistent hours due to the missing day. Adding time zone parameters fixes this but complicates APIs, especially on devices crossing time zone boundaries, like aircraft. These real-world quirks force Kotlin to balance simplicity with correctness, often at the cost of elegance.
Daylight Saving Time: Bug Hunting’s Legacy
Daylight saving time (DST) adds further headaches. Vsevolod traced DST to George Hudson, a 19th-century entomologist who proposed time shifts to hunt bugs in New Zealand. During DST transitions, times like 2:30 AM may not exist (spring forward) or occur twice (fall back), breaking datetime calculations. An anecdote about Vsevolod’s friend Vlad, whose smart home set a 9:30 AM alarm instead of 8:30 AM during a DST shift, underscored the need for explicit API parameters to handle ambiguous times, sacrificing simplicity.
API Design: Slow and Steady Wins
Vsevolod concluded that Kotlin’s libraries evolve slowly to avoid mistakes. Rushed APIs lead to bugs, like Vlad’s missed alarm, requiring years of maintenance. Thoughtful design, considering edge cases like locales, time zones, and DST, ensures reliability but delays releases. Twitter polls and community feedback help, but the real world’s complexity—political, historical, and cultural—demands patience. By prioritizing robustness, Kotlin’s team crafts APIs that won’t “get anyone fired,” even if it means fewer “nice things” per release.
Links:
[KotlinConf2024] Exploring Exposed: Kotlin’s Database Solution
At KotlinConf2024, Chantal Loncle, a JetBrains Exposed team member, introduced Exposed, an open-source Kotlin library simplifying database access. Since its 2016 debut as a JetBrains internal tool, Exposed has grown into a versatile abstraction layer, supporting multiple databases with type-safe SQL and minimal boilerplate. Chantal guided attendees through configuring connections, defining schemas, manipulating data via DSL, DAO, and plain SQL, and extending functionality with custom features, showcasing Exposed’s flexibility for Kotlin developers.
Why Exposed? Solving Database Pain Points
Exposed addresses common database access challenges, offering a balance between control and abstraction. Chantal explained that developers often need to map Kotlin objects to database records without complex SQL or frameworks requiring annotations. Exposed supports this by providing type-safe interactions, reducing boilerplate, and abstracting database differences. Its cuttlefish mascot reflects its adaptability, enabling database-agnostic apps. Whether needing high-level abstraction or low-level SQL control, Exposed’s consistent API caters to diverse needs, as seen in its growing popularity, ranking sixth among JetBrains’ GitHub repositories.
Setting Up Database Connections
Connecting to a database with Exposed is straightforward yet flexible. Chantal demonstrated the databaseConnect
function, which accepts a connection URL or data source and relies on JDBC drivers. This function doesn’t establish a connection immediately but configures details for later use. Developers can set parameters via a config builder for specific database needs or create multiple connections by storing instances. This approach ensures Exposed handles underlying database variations, allowing developers to focus on application logic rather than connection management.
Transactions: The Heart of Data Operations
Transactions are central to Exposed’s data manipulation. Chantal illustrated how the transaction
function wraps operations like schema creation or data inserts, accepting a database instance or defaulting to a registered one. For multiple connections, developers can set a default database or override settings per transaction. Transactions ensure atomicity, committing or rolling back changes, and Exposed’s transaction manager simplifies resource handling. This design supports both simple and complex workflows, with utility functions like SchemaUtils.create
generating SQL tailored to the target database.
Defining Schemas with Table Objects
Schema definition in Exposed revolves around the Table
class, which maps Kotlin data classes to database tables. Chantal used a docking bay app example, tracking ships and planets. For a Planets
table, developers register columns (e.g., auto-incrementing Long
for IDs, String
for names) and constraints like primary keys. For a Ships
table, a foreign key references Planets
, establishing a one-to-many relationship. Exposed’s SchemaUtils
handles table creation or updates, checking existing schemas to generate necessary SQL, ensuring compatibility across databases like PostgreSQL or MySQL.
Data Manipulation: DSL and DAO Approaches
Exposed offers two data manipulation approaches: domain-specific language (DSL) and data access object (DAO). Chantal showcased DSL’s SQL-like syntax, where extension functions like insert
or selectAll
on table objects build type-safe queries. For example, inserting a ship involves assigning column values, with autogenerated keys accessible post-insertion. The DAO approach, less verbose, maps records to entity classes, abstracting SQL further. Developers call new
on entities instead of insert
, manipulating fields directly. Both methods, switchable via configuration, support complex queries, joins, and updates, catering to different preferences.
Plain SQL and Extensibility
For developers needing direct SQL control, Exposed’s exec
function runs raw SQL strings, as Chantal demonstrated with a stored function to calculate maximum planet distances. Beyond core functionality, Exposed shines in extensibility. Statement interceptors allow custom logic, like logging deletions, at specific lifecycle points. Entity hooks in DAO mode achieve similar customization. Custom column types, such as a PostgreSQL IntRange
for pricing, extend Exposed to unsupported data types. These features, supported by open classes, empower developers to tailor Exposed to unique requirements, enhancing its versatility.
Future of Exposed: Roadmap and Community
Exposed’s roadmap reflects its community-driven evolution, with 43 new contributors in 2023 halving the GitHub issue backlog. Chantal highlighted plans for a stable 1.0 release, improved documentation with tutorials, and migration support via integration with tools like Flyway. IDE tooling will autogenerate table and entity classes, reducing boilerplate, while R2DBC support will enable non-blocking database operations. Community feedback remains vital, as seen in Q&A discussions on migration scripts, ensuring Exposed continues to meet developer needs across diverse applications.
Links:
[KotlinConf2024] Lifecycles, Coroutines, and Scopes: Structuring Kotlin
At KotlinConf2024, Alejandro Serrano Mena, a JetBrains language evolution researcher, delivered a re-recorded talk on structured concurrency, illuminating how coroutine scopes bridge Kotlin’s concurrency model with framework lifecycles. Exploring Compose, Ktor, and Arrow libraries, Alejandro demonstrated how scopes ensure intuitive job cancellation and supervision. From view model scopes in Compose to request scopes in Ktor and resource management in Arrow, the talk revealed the elegance of scope-based designs, empowering developers to manage complex lifecycles effortlessly.
Structured Concurrency: A Kotlin Cornerstone
Structured concurrency, a pillar of Kotlin’s coroutine library, organizes jobs within parent-child hierarchies, simplifying cancellation and exception handling. Alejandro explained that unlike thread-based concurrency, where manual tracking is error-prone, scopes make concurrency a local concern. Jobs launched within a CoroutineScope
are tied to its lifecycle, ensuring all tasks complete or cancel together. This model separates logical tasks (what to do) from execution (how to run), enabling lightweight job scheduling without full threads, as seen in dispatchers like Dispatchers.IO
.
Coroutine Scopes: Parents and Children
A CoroutineScope
acts as a parent, hosting jobs created via launch
(fire-and-forget) or async
(result-producing). Alejandro illustrated this with a database-and-cache example, where a root job spawns tasks like saveToDatabase
and saveToCache
, each with subtasks. Cancellation propagates downward—if the root cancels, all children stop. Exceptions bubble up, triggering default cancellation of siblings unless a SupervisorJob
or CoroutineExceptionHandler
intervenes. Scopes wait for all children to complete, ensuring no dangling tasks, a key feature for frameworks like Ktor.
Compose: View Model Scopes in Action
In Jetpack Compose, view model scopes tie coroutines to UI lifecycles. Alejandro showcased a counter app, where a ViewModel
manages state and launches tasks, like fetching weather data, within its viewModelScope
. This scope survives Android events like screen rotations but cancels when the activity is destroyed, preventing job leaks. Shared view models across screens maintain state during navigation, while screen-specific view models clear tasks when their composable exits, balancing persistence and cleanup in multiplatform UI development.
Ktor: Scopes for Requests and Applications
Ktor, a Kotlin HTTP framework, leverages coroutine scopes for server-side logic. Alejandro demonstrated a simple Ktor app with GET and WebSocket routes, each tied to distinct scopes. The PipelineContext
scope governs individual requests, while the Application
scope spans the server’s lifecycle. Launching tasks in the request scope ensures they complete or cancel with the request, enabling fast responses. Application-scoped tasks, like cache updates, persist beyond requests, offering flexibility but requiring careful cancellation to avoid lingering jobs.
Arrow: Beyond Coroutines with Resource and Saga Scopes
The Arrow library extends scope concepts beyond coroutines. Alejandro highlighted resource scopes, which manage lifecycles of database connections or HTTP clients, ensuring automatic disposal via install
or autoCloseable
. Saga scopes orchestrate distributed transactions, pairing actions (e.g., increment) with compensating actions (e.g., decrement). Unlike coroutine scopes, which cancel on exceptions, saga scopes execute compensations in reverse, ensuring consistent states across services. These patterns showcase scopes as a versatile abstraction for lifecycle-aware programming.
Dispatching: Scheduling with Flexibility
Coroutine scopes delegate execution to dispatchers, separating task logic from scheduling. Alejandro noted that default dispatchers inherit from parent scopes, but developers can specify Dispatchers.IO
for I/O-intensive tasks or Dispatchers.Main
for UI updates. This decoupling allows schedulers to optimize thread usage while respecting structured concurrency rules, like cancellation propagation. By choosing appropriate dispatchers, developers fine-tune performance without altering the logical structure of their concurrent code.
Conclusion: Think Where You Launch
Alejandro’s key takeaway—“think where you launch”—urges developers to consider scope placement. Whether in Compose’s view models, Ktor’s request handlers, or Arrow’s resource blocks, scopes simplify lifecycle management, making cancellation and cleanup intuitive. By structuring applications around scopes, Kotlin developers harness concurrency with confidence, ensuring robust, maintainable code across diverse frameworks.
Links:
[KotlinConf2024] Kotlin 2.0 and Beyond: Evolving Language Features
Michail Zarečenskij, Kotlin’s lead language designer, captivated KotlinConf2024 with a deep dive into Kotlin 2.0’s advancements and future features. The K2 compiler, central to Kotlin 2.0, introduces a frontend intermediate representation (FEIR) and a new control flow engine, enhancing code consistency and smart casts. Michail outlined upcoming features like guarded conditions, context parameters, and union types for errors, addressing modern development challenges. Through code examples and audience Q&A, he showcased Kotlin’s evolution, ensuring it remains concise, safe, and expressive for millions of developers.
Kotlin’s Evolutionary Journey
Since its 1.0 release eight years ago, Kotlin has grown significantly, adding features like coroutines, functional interfaces, and multiplatform support post-launch. Michail highlighted gradual introductions, such as trailing commas and exhaustive when statements, alongside bug fixes for smart casts and type inference. Beyond language, Kotlin targets JVM, Native, and Web, with scripting, Android, and server-side capabilities. Supported by IntelliJ and Fleet plugins, Compose compiler plugins, and libraries like Serialization, Kotlin’s ecosystem thrives on community-driven open-source contributions, setting the stage for K2’s transformative impact.
The K2 Compiler: A Robust Foundation
The K2 compiler, powering Kotlin 2.0, addresses limitations of the original compiler, which struggled with unexpected features like multiplatform requirements. Michail explained K2’s redesigned architecture, enabling faster language evolution and multiplatform plugin support. Unlike the tightly coupled original, K2 separates compiler and IDE logic, simplifying maintenance. With over 80 features, including build tool enhancements, K2 prioritizes performance, cutting compilation times, and correctness, fixing longstanding issues. Tested on 10M lines of code, K2 ensures stability, making it a cornerstone for future language advancements.
Frontend Intermediate Representation: Consistent Code
K2’s frontend intermediate representation (FEIR) transforms complex language constructs into simpler forms earlier in compilation, ensuring consistent analysis. Michail demonstrated with a mutable list increment example, where K2 resolves operator and conversion issues that tripped the old compiler. By desugaring expressions, FEIR handles nullable operators and delegate properties robustly, supporting intricate combinations of operators and extensions. This consistency empowers developers to compose features confidently, reducing errors in scenarios like nullable assignments or generic type operations, strengthening Kotlin’s expressiveness.
Control Flow Engine: Smarter Analysis
The new control flow engine in K2 enhances code execution analysis, detecting unreachable code and potential bugs. Michail showcased improved smart casts, such as local variables contributing to type safety. For example, extracting a nullability check to a variable now supports smart casts, unlike the old compiler. Inline functions gain implicit “call-in-place” contracts, enabling smart casts in lambdas. Logical operator smart casts, like merging types after an “or” check, further refine type inference, making Kotlin’s type system more intuitive and reducing manual casts.
Enhanced Smart Casts in Kotlin 2.0
Smart casts, a Kotlin hallmark, see significant upgrades in 2.0. Michail presented examples where K2 applies smart casts across nullability checks, type checks, and inline function lambdas. For instance, checking a variable’s type and nullability now triggers dual smart casts in appropriate blocks. Logical “or” operations infer supertypes, enabling method calls without explicit casting. These enhancements reduce cognitive load, letting developers focus on logic rather than type management. Compatibility with existing smart casts and contracts ensures a seamless transition, boosting code safety.
Guarded Conditions: Concise Control Flow
Set for beta in Kotlin 2.1, guarded conditions in when expressions eliminate restrictive single-check limitations. Michail illustrated with a UI-rendering example, where repeated variable checks cluttered code. Guarded conditions allow additional “if” clauses in when branches, reducing repetition and nesting. Context-sensitive resolution, planned for Kotlin 2.2, further simplifies sealed type handling by omitting base class names when types are known. These features streamline control flow, enhancing readability and maintainability, especially in complex UI or data-processing logic.
Context Parameters: Flexible APIs
Context parameters, moving to beta in Kotlin 2.2, enhance API design by allowing multiple receivers. Michail demonstrated with an autoclose scope, where context parameters enable extension functions within specific scopes, improving IDE autocompletion. This addresses limitations in single-receiver functions, making APIs more extensible and discoverable. By moving receivers to a context section, developers gain flexibility in defining operations, aligning with Kotlin’s focus on expressive, type-safe APIs. The feature’s popularity in experimental form underscores its potential to reshape library design.
Union Types for Errors: Robust Error Handling
Michail previewed union types for errors, targeting error and exception handling without general union types due to type checker complexity. In a sequence search example, union types distinguish “not found” from “null” results, eliminating extra variables and unchecked casts. Planned for future releases, this feature introduces a dedicated error type category with a “throw” method, compatible with exceptions. Smart casts automatically apply, streamlining error handling. Q&A clarified that multicatch support, akin to Java, is a goal, enhancing Kotlin’s robustness in production code.
Links:
[KotlinConf2024] KotlinConf2024 Keynote: Powering the Future with Kotlin 2.0
KotlinConf2024, hosted in Copenhagen, welcomed 2,000 attendees and thousands online, kicking off with a vibrant keynote celebrating Kotlin’s evolution. Igor Tolstoy, Kotlin Project Lead at JetBrains, unveiled Kotlin 2.0, powered by the K2 compiler, promising double compilation speeds and robust multiplatform capabilities. Joined by speakers from Meta, Google, Amazon, and JetBrains, the keynote showcased Kotlin’s adoption in 20M lines of code at Meta, Google’s multiplatform push, and Amazon’s AWS SDK. From Compose Multiplatform to AI-driven tools, the event underscored Kotlin’s role in modern development, fueled by a thriving ecosystem.
A Global Stage for Kotlin Innovation
KotlinConf2024 buzzed with energy, uniting 2,000 in-person attendees, online viewers, and 71 global events across 37 countries. The conference featured five parallel sessions, lightning talks, a coach challenge, and code labs by Touchlab. A lively party with a live band and quiz, plus a closing panel, kept spirits high. Attendees donned T-shirts with words like “love,” “code,” and “nothing,” encouraged to form phrases for social media with #KotlinConf. Sponsors, including American Express, powered the event, with their booths bustling in the exhibit hall. The KotlinConf app, built with Compose Multiplatform, guided attendees, urging them to vote on sessions to shape future lineups.
Kotlin 2.0: The K2 Compiler Revolution
Igor Tolstoy introduced Kotlin 2.0, a milestone driven by the K2 compiler. This rewrite delivers a 2x compilation speed boost, slashing wait times for builds. Tested across 10M lines of code from 40 JetBrains and community projects, K2 ensures stability, with 18,000 developers and companies like Meta adopting early versions. The IntelliJ K2 mode, nearing beta, accelerates code highlighting by 1.8x, set to become default in IntelliJ 24.3. Avoiding major syntax changes, K2 fixes longstanding issues, enhancing code consistency and enabling faster language evolution without breaking existing projects.
Meta’s Kotlin Journey: Scaling Android
Eve Maler, an Android engineer from Meta, shared their Kotlin adoption, now spanning 20M lines of code. Since embracing Kotlin-first development three years ago, Meta reduced code by 10%, boosting reliability and developer preference. K2’s incremental compilation cut build times by up to 20%, with 95% of modules now using K2. Tools like IntelliJ’s J2K converter automate Java-to-Kotlin transitions, converting tens of thousands of lines weekly. Meta’s frameworks, including Litho and Dex optimizations, fully support Kotlin, paving the way for a mono-language Android experience, enhancing developer productivity.
Google’s Multiplatform Commitment
Jeffrey van Gogh from Google highlighted their investment in Kotlin, with 33M lines of code internally, doubling since 2023. Kotlin 2.0’s stability thrilled Google, who contributed compiler fixes and ported tools like Android Lint and Compose plugins to K2. The Compose compiler plugin now ships with Kotlin distributions, simplifying updates. Google announced official Android support for Kotlin Multiplatform (KMP) at Google I/O, enabling shared business logic across mobile, web, and desktop. Jetpack libraries like Room and DataStore now support KMP, with Android Studio integrating native KMP tooling, signaling a hybrid model balancing native and shared code.
Compose Multiplatform: Cross-Platform UI
Sebastian Aigner and Ekaterina Petrova celebrated Compose Multiplatform’s stability on Android and desktop, with iOS nearing beta and web in alpha. Used in thousands of apps, including McDonald’s, Compose reduced crashes and unified teams by sharing business logic. New APIs, like Jetpack Navigation and type-safe resources, enhance cross-platform development. iOS-specific improvements, such as VoiceOver integration and refined scroll physics, ensure native experiences. Web support leverages Kotlin/Wasm for high-performance browser apps. Compose’s flexibility lets developers choose how much code to share, from logic to full UI, meeting users across platforms.
Tooling Evolution: Amper and Fleet
JetBrains introduced Amper, a new build tool simplifying multiplatform project setup with minimal configuration. A Kotlin JVM project requires just one line, with dependencies easily added. Amper integrates with IntelliJ and Android Studio, offering quick fixes for project creation. Fleet, a preview multiplatform IDE, unifies Kotlin and Swift development, supporting Xcode projects and cross-language debugging. These tools automate environment checks, provide UI previews, and integrate JetBrains’ AI Assistant for code generation, streamlining workflows and lowering barriers for KMP adoption.
Ecosystem Growth: Libraries and AWS
The Kotlin ecosystem thrives, with a 50% rise in open-source multiplatform solutions. Libraries like Ktor, Serialization, and DateTime gain multiplatform APIs, while new additions like Kandy (data visualization) and DataFrame (data processing) expand capabilities. Amazon’s Julia detailed their AWS SDK for Kotlin, now generally available, built on Smithy for idiomatic APIs. Supporting hundreds of services, including Amazon Bedrock, the SDK leverages coroutines for pagination and streams. Amazon’s internal Kotlin use surged 6x, with teams like Prime Video reporting higher quality and productivity.
AI-Powered Development with JetBrains
Svetlana Isakova closed with JetBrains’ AI Assistant, written in Kotlin and integrated into IntelliJ and Fleet. It offers context-aware code completion, refactoring, and explanations, understanding project structures and dependencies. A Kotlin-specific language model, trained on open-source repositories, powers precise code generation, outperforming larger models in benchmarks. Available in IntelliJ 24.2, it supports multi-line completion and custom contexts. For enterprises, an on-premises version ensures compliance. Open-sourced datasets on Hugging Face further Kotlin’s AI advancements, equipping developers for the AI-driven future.