The programming language after Kotlin – with the creator of Kotlin

The Pragmatic Engineer 1h44 9 min #64
The programming language after Kotlin – with the creator of Kotlin
Watch on YouTube

Summary

  • Andrey Brela, the creator of Kotlin, discusses his journey designing one of the world’s most popular programming languages and his new project, CodeSpeak — a language built on English for an era where AI writes most of the code.
    • Kotlin was born out of frustration with Java’s stagnation in the early 2010s and was designed by deliberately borrowing proven ideas from Scala, C#, and Groovy, rather than inventing new concepts.
    • Kotlin’s seamless interoperability with Java was one of the hardest engineering challenges, requiring years of work to make two languages feel like one.
    • Google’s 2017 announcement making Kotlin the official language for Android — which surprised even the Kotlin team — sent adoption from tens of thousands to millions of users almost overnight.
    • Andrey left JetBrains in 2020 and is now building CodeSpeak, a language based on natural English that leverages LLMs to let engineers communicate intent rather than spelling out every line of code.

Why Kotlin was created

  • By 2010, Java had not seen a major language update since Java 5 in 2004, while C# had already adopted lambdas, properties, and other modern features.
    • Java 6 made no language changes; Java 7 made only minor ones. Lambdas for Java were stuck in development and wouldn’t arrive until 2014.
    • Meanwhile, Scala and Groovy existed but had problems: Groovy was too dynamic for large-scale production, and Scala was powerful but had a slow compiler, heavy use of implicits, and was difficult to build tooling for.
  • JetBrains — whose developers used and built tools for both Scala and Groovy — saw a market opportunity for a statically-typed language that combined the best ideas from existing languages with world-class tooling.
    • They had a unique advantage: direct access to millions of developers through IntelliJ IDEA and the trust that came with it.
  • Andrey was initially skeptical (“you don’t just make a new language”) but was convinced after months of technical discussions with the JetBrains team in St. Petersburg.

Dynamic vs. static languages

  • Dynamic languages (Python, Ruby, JavaScript, Groovy) let you start building quickly because the compiler doesn’t restrict you, but they make large-scale refactoring, navigation, and correctness guarantees much harder as projects grow.
  • Static languages (Java, Kotlin, Scala) enable precise refactoring tools and catch entire categories of errors at compile time, making them better suited for large teams and long-lived codebases.
  • Kotlin was designed from the start as a static language targeting large-scale production use.

Designing Kotlin

  • The design process began with months of whiteboard discussions at JetBrains, drawing on feedback from many developers about pain points in Java.
    • Andrey compiled lists of things to fix, sketched features, and gradually fit the pieces together — largely in his head.
    • Early ideas included fully fledged multiple inheritance, which was dropped after someone outside JetBrains explained the intractable complexity of constructor initialization order.
  • Development started not with a compiler but with an IntelliJ IDEA plugin, leveraging IntelliJ’s world-class parsing infrastructure to prototype the language’s syntax and tooling before anything could compile.
    • The front end (parsing, type checking) and bytecode generation were split across different team members.
  • The team started part-time — Andrey was still a PhD student — and he eventually dropped his PhD to work on Kotlin full-time, driven by hubris and youthful confidence.

Why it’s named Kotlin

  • The language was originally going to be called “Jet,” but that name was already trademarked by a compiler project in Novosibirsk, Russia.
    • Naming was described as one of the worst parts of the process: all good names were taken, and many un-taken names weren’t searchable.
    • Dmitry Jemerov looked out the window at Kotlin Island in the Gulf of Finland near St. Petersburg and suggested “Kotlin” — it was googable, recognizable, and not already widely used.
    • The team was hesitant (the word has negative connotations in German and Mandarin) and initially announced it as “Project Kotlin” as a code name with room to change it, but it stuck.

Kotlin vs. Java

  • The initial selling points over Java were:
    • Less verbosity: no more public static void main, no repeated type declarations (e.g., List<String> strings = new ArrayList<String>() became val strings = ArrayList<String>() with type inference).
    • Lambdas: Java didn’t have them until 2014; Kotlin had them from the start.
    • No semicolons: following the trend of modern languages.
    • Null safety: added after Roman Elizarov (who later became Kotlin’s project lead) told the team at an internal presentation that solving null safety for enterprise developers would be transformative.
      • Null pointer exceptions are the most common runtime error in Java. Tony Hoare called null references his “billion-dollar mistake.”
      • Kotlin enforces null safety at compile time using nullable (String?) vs. non-null (String) types, but at runtime nullable references are the same as Java references — no extra allocation overhead.
      • The safe call operator (?.) propagates nulls instead of throwing exceptions.
    • Extension functions: borrowed from C#, allowing developers to add methods to existing classes without inheritance.

Kotlin’s influences

  • Scala: primary constructors, data classes, val/var distinction, and variance annotations were all adapted from Scala. Andrey called Scala the biggest influence after Java.
    • Kotlin simplified some Scala features: traits with state were dropped in favor of interfaces with method bodies only, avoiding complex constructor ordering.
  • C#: extension functions and a parser hack for generic angle brackets in expression contexts (resolving the ambiguous < / > grammar that plagues C++ and Java).
  • Groovy: type-safe builders inspired by Groovy’s builder patterns, leading to Kotlin’s extension function types and trailing lambda syntax.
  • Gosu: inspired Kotlin’s smart casts — after an if (x is String) check, the compiler automatically treats x as a String inside the block, eliminating redundant casts.

Features Kotlin left out

  • Pattern matching: Kotlin had an early design inspired by functional languages but postponed it because implementing it required creating an entire parallel syntax universe. Smart casts plus the when expression gave ~80% of the benefit for normal developers. Pattern matching was later added in a limited form.
  • Ternary operator (? :): Andrey considers omitting it one of his biggest regrets. Kotlin made if an expression (so if (a) b else c works), which made the ternary operator seem redundant. But if as an expression is more verbose for simple cases, and the ternary operator couldn’t be retrofitted later because ? was already used for nullable types and : for type annotations.

Java interoperability

  • Java interop was the single most time-consuming feature. The goal was bidirectional: calling Java from Kotlin and Kotlin from Java should be completely transparent.
    • Compilation trick: Kotlin compiles first, producing class files that the Java compiler can read. The Kotlin compiler includes a Java front end to resolve Java sources in mixed projects.
    • Incremental compilation: managing incremental builds across two languages simultaneously is extremely complex and full of corner cases.
    • Collections: Kotlin distinguishes between read-only and mutable collections, but Java only knows mutable collections. The Kotlin compiler uses a trick layer that presents collections to Java with proper variance annotations (e.g., List<? extends String>) so that a Kotlin method accepting List<Any> can accept List<String> from Java.
    • Nullability across the boundary: Initially, all Java types were treated as nullable in Kotlin, which produced horrible developer experience with null checks everywhere. Andrey worked with a type theorist (Ross from Pernell) to develop “platform types” — types from Java that are neither nullable nor non-null, letting the developer decide. The mathematical elegance was lost in implementation, but it works well in practice.

The Kotlin timeline

  • Development started in autumn 2010; Kotlin 1.0 was released in February 2016 — roughly 5 years.
    • Andrey consistently predicted “one year from now” and had no real project management plan.
    • The initial team was about 4 part-time people, many of them Andrey’s former students fresh out of university. By release, the team was around 25 people. By the time Andrey left in 2020, it was about 100 people (70 engineers).
    • The initial prototype was eventually almost completely rewritten over about 6 years.

Kotlin’s development process

  • Bootstrapping: The Kotlin compiler is written in Kotlin, so building a new version requires a previous version of the compiler. Reproducing builds from scratch is difficult because some intermediate compiler versions were one-off hacks committed to branches and then discarded.
  • Code reviews: done from early on, often in person — Andrey preferred sitting together with reviewers.
  • Open issue tracker: anyone could submit bugs and feature requests, which was valuable but hard to manage at scale.
  • Backward compatibility: a major concern that delayed 1.0. The team invented tooling for automatic migration between milestone builds and conducted a year-long design review before 1.0 to anticipate future changes and prohibit code patterns that would block them.
    • “Message from the future”: a mechanism where a newer compiler writes metadata into binaries telling an old compiler which specific methods it can’t understand, rather than invalidating the entire library.
    • Experimental features: Kotlin pioneered the practice of marking features as experimental, requiring explicit opt-in via compiler flags and producing warnings. This has since been adopted by other languages including Java.

How Android became Kotlin-first

  • Kotlin was originally targeted at server-side Java developers, not Android developers. Android was an afterthought.
    • An early user reported that the Android toolchain crashed with Kotlin-generated bytecode because Android’s tools faithfully implemented the JVM spec, unlike the HotSpot VM which was lenient about invalid flags. This made Android a valuable testing environment for Kotlin’s bytecode correctness.
  • Android developers were stuck on old Java versions because updating the VM across billions of devices was impractical. Java 8 features arrived in 2014 but were unavailable to many Android developers.
    • iOS’s transition from Objective-C to Swift showed that a major platform could successfully adopt a modern language.
    • Google’s switch from Eclipse to IntelliJ as the base for Android Studio meant Kotlin’s IntelliJ plugin worked seamlessly for Android development too.
  • Google reached out in 2016 about announcing official Kotlin support at Google I/O 2017 — just three months away. The Kotlin team and Google engineers pulled off a heroic effort to make it happen.
    • The Kotlin Foundation was created to govern the language, and Google held the Kotlin trademark for one year as a legal guarantee during the transition.
    • Usage went from tens of thousands to millions of users in the year following the announcement.

CodeSpeak: a language for the AI era

  • Andrey’s new project, CodeSpeak, is a programming language based on English designed for a world where AI writes most of the code.
    • The core insight: there is a gap between what humans understand and what compilers require. LLMs understand much more like humans do, so we can shrink the amount of information a programmer needs to tell the computer — Andrey estimates about 10x less code for many projects.
    • CodeSpeak is not a fully formal language. It uses natural language as the “query language” to leverage LLMs as a library of all the world’s code.
    • The team is discovering the language by finding the shortest English descriptions of existing code that can generate equivalent implementations, then verifying that small code changes produce small spec changes.
  • Andrey does not believe in designing a new language specifically for LLMs to write:
    • LLMs need large training sets, which don’t exist for new languages. They write established languages (Python, Java) better than newer ones (Rust, Kotlin).
    • LLMs are trained on human language; their power comes from exposure to all existing code. Inventing a new language for them is not promising.
    • Instead, CodeSpeak is designed for humans — to preserve intent in a form that both humans and LLMs can work with.

How software engineering is changing with AI

  • We are in the early days of coding agents. Tools like Claude Code and Cursor Agent are breakthroughs, but there are inherent problems:
    • Lost intent: you communicate with an agent in natural language, but what gets committed is code. The conversation — your actual intent — disappears. Your teammates see machine language, not human language.
    • Review burden: teams are generating more code than they can review. Some suggest skipping code review entirely, but this is unsustainable. The alternative is investing heavily in automated testing.
  • Andrey believes the future still involves engineers managing complexity:
    • Models will get smarter, but humans won’t. As long as humans are in charge (i.e., no technological singularity), engineers will need to communicate intent and organize complexity.
    • Essential complexity — how systems should behave — won’t go away even if accidental complexity (implementation boilerplate) is reduced by AI.
    • Teams may become smaller and more powerful, but the engineering mindset will remain essential.

Developer tools of the future

  • 2025 will be the year agents gain access to developer tools (language servers, code navigation, indexed databases) that IDEs have had for years. Currently, agents like Claude Code lack these capabilities and burn more tokens as a result.
  • More specialized development environments will be built from the ground up to work with agents, going beyond terminal-based tools.
  • Better review tools and testing tools are needed but will take time to develop.
  • Andrey’s advice for engineers feeling scared by AI:
    • Some current trends (like not hiring juniors) are short-sighted and will be rolled back.
    • Invest time in learning to use AI tools well — there is real skill involved, and productivity gains are significant for those who master them.
    • For new grads: either become incredibly productive at shipping robust code, or go deep into hard technical areas to build rare expertise. Looking under the hood and understanding how things work pays off broadly.
Back to The Pragmatic Engineer