What is KSP (Kotlin Symbol Processing), and what are the two entry-point types a processor author must implement?
answer
- Provider = factory, Processor = worker
- create(environment) -> process(resolver)
- META-INF/services registers the Provider
- No Java stubs; Kotlin-native symbol model
- ksp(...) Gradle configuration, not implementation
basics
~10 sKSP is Kotlin's tool for reading code at compile time and generating new source files. You write a SymbolProcessor that does the work, and a SymbolProcessorProvider that creates it.
solid answer
~30 sKSP (Kotlin Symbol Processing) is a Kotlin-native API for compile-time metaprogramming: it lets you inspect annotated declarations and generate new Kotlin/Java source files. Two types are mandatory. SymbolProcessorProvider is the factory registered via a META-INF/services file (or the KSP Gradle plugin); KSP instantiates it once and calls create(environment), passing a SymbolProcessorEnvironment that exposes the CodeGenerator, KSPLogger, and options. It returns a SymbolProcessor, whose process(resolver: Resolver): List<KSAnnotated> method is invoked each round. The Resolver is the query API over the program's symbols. Unlike kapt, KSP reads Kotlin directly through its own symbol model, so it never generates Java stubs.
code
kotlin · 14 linesclass BuilderProcessorProvider : SymbolProcessorProvider {
override fun create(env: SymbolProcessorEnvironment): SymbolProcessor =
BuilderProcessor(env.codeGenerator, env.logger)
}
class BuilderProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger,
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
logger.info("KSP round running")
return emptyList()
}
}go deeper
Can state KSP is compile-time, names SymbolProcessor and SymbolProcessorProvider, and knows it generates source files.
Explains the Provider/Processor split, the create->process flow, and how registration via META-INF/services and the ksp(...) configuration works.
Contrasts KSP's Kotlin-native symbol model with stub generation, discusses the SymbolProcessorEnvironment contents, and rounds/deferral semantics.
Reasons about processor design for incrementality and multimodule builds, version-compatibility of the KSP API surface, and library packaging of processors.
## What KSP is **KSP (Kotlin Symbol Processing)** is a Kotlin compiler plugin and API for **compile-time metaprogramming**. A *symbol processor* reads the *symbols* (declarations like classes, functions, properties) in the program being compiled, and can **generate new source files**. Typical uses: generating boilerplate, builders, serializers, or DI wiring from annotations. KSP models Kotlin's own language constructs (it understands `suspend`, nullability, extension functions, type aliases) instead of going through Java's `javax.lang.model`. Crucially, it **does not produce Java stubs**, which is what makes it faster than the older kapt approach. ## The two entry-point types ```kotlin // 1) The factory — registered & discovered by KSP class MyProcessorProvider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = MyProcessor(environment.codeGenerator, environment.logger, environment.options) } // 2) The worker — does the inspection and code generation class MyProcessor( private val codeGenerator: CodeGenerator, private val logger: KSPLogger, private val options: Map<String, String>, ) : SymbolProcessor { override fun process(resolver: Resolver): List<KSAnnotated> { // query the program, emit files, return deferred symbols return emptyList() } } ``` - **`SymbolProcessorProvider`** — the registered factory. KSP discovers it via `src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider` (one fully-qualified class name per line). Its `create(environment)` receives a **`SymbolProcessorEnvironment`** exposing `codeGenerator`, `logger` (`KSPLogger`), `options`, and the KSP/Kotlin version. It returns the processor. - **`SymbolProcessor`** — the worker. Its single required method `process(resolver: Resolver): List<KSAnnotated>` runs the actual logic. The returned list is the set of symbols that **could not be processed yet** (deferred to a later round). ## How it is wired in Gradle Apply the `com.google.devtools.ksp` plugin and declare the processor with the `ksp(...)` configuration (not `implementation`): ```kotlin plugins { id("com.google.devtools.ksp") version "<ver>" } dependencies { ksp("com.example:my-processor:1.0") } ``` ## Key terms - **Symbol** — a declaration the compiler knows about (class, function, property, parameter). - **Round** — one pass of `process()`; KSP runs multiple rounds so newly generated code can itself be processed. - **Generated sources** — files written through the `CodeGenerator`, automatically added to the compilation.
- Where do you register a SymbolProcessorProvider so KSP finds it?In src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider, listing the provider's fully-qualified class name.
- Which Gradle configuration declares a KSP processor dependency?The ksp(...) configuration added by the KSP Gradle plugin, e.g. ksp("group:artifact:version"); using implementation would put it on the runtime classpath instead of running it as a processor.
The Provider is the staffing agency that hires one worker (the Processor); each round the worker is handed a Resolver — a searchable index of the codebase — and writes new files.
saying these in an interview costs you the question
- Thinking KSP runs at runtime via reflection instead of at compile time
- Claiming you implement only one type — forgetting the Provider factory
- Saying KSP generates Java stubs like kapt does
- Registering the Processor (not the Provider) in META-INF/services
- Declaring the processor with implementation instead of ksp(...)