Your organization has several plugins that emit dozens of logger.warn warnings. How would you adopt the Problems API across them, and what design choices keep the resulting diagnostics coherent?
answer
- diagnostics contract, not refactor
- group taxonomy: root per plugin, nested by concern
- stable category-level ids (no variable data)
- centralize injection -> isolates incubating API
- incremental warn->report migration; govern id catalog
basics
~20 sDefine a shared ProblemGroup taxonomy and stable ProblemId naming across plugins, inject Problems through a common base, migrate warnings incrementally (keeping ids stable), and keep occurrence-specific data in contextualLabel/details/location so consumers can group and aggregate consistently.
solid answer
~50 sTreat the Problems API rollout as defining a **diagnostics contract**, not a per-plugin afterthought. First, design a **group taxonomy**: a root `ProblemGroup` per plugin, nested sub-groups by concern (naming, deps, schema), so consumers see a coherent tree. Second, establish a **ProblemId naming convention** with stable, category-level keys — never embed file/version/value into the id, since that breaks grouping. Third, centralize **injection**: a shared base plugin/task that receives `Problems` and exposes helpers, so every plugin reports the same way. Fourth, **migrate incrementally**: convert each `logger.warn` into a `report` with the right severity, preserving message text in `contextualLabel`/`details` and adding a `location` where possible; reserve `throwing` for genuine errors. Because the API is incubating in 8.x, isolate the calls behind your helper so an API change touches one place. Finally, since the same ids now drive console dedup, the HTML report, IDE annotations and scans, govern the id catalog like a public API — keep ids stable across releases so dashboards and quick-fix mappings don't break.
code
kotlin · 14 lines// Shared helper isolates the incubating API and standardizes reporting
abstract class AcmeDiagnostics @Inject constructor(
private val problems: Problems
) {
private val root = ProblemGroup.create("acme", "Acme Plugins")
fun group(name: String, display: String) = ProblemGroup.create(name, display, root)
fun report(id: ProblemId, label: String, details: String? = null,
severity: Severity = Severity.WARNING) =
problems.reporter.report(id) { s ->
s.contextualLabel(label).severity(severity)
if (details != null) s.details(details)
}
}go deeper
Recognize that warnings can be migrated to report() calls.
Map each warn to a report with severity, label, details and location.
Design the group/id taxonomy and a shared injection helper, migrating incrementally.
Govern the id catalog as an org-wide diagnostics contract consumed by console/HTML/IDE/scan, and isolate the incubating API behind one seam.
## Frame it as a contract, not a refactor Once problems are structured, their **ids become a public taxonomy** consumed by the console (dedup/counts), the HTML report, IDEs (inline annotations, quick-fix mapping), and build scans (aggregation/trending). So the rollout's hard part isn't the API calls — it's designing a stable, coherent taxonomy. ## 1. Group taxonomy Give each plugin a root group and nest by concern: ```kotlin val root = ProblemGroup.create("acme-lint", "Acme Lint") val naming = ProblemGroup.create("naming", "Naming", root) val deps = ProblemGroup.create("deps", "Dependencies", root) ``` Consumers can then render a clean tree: *Acme Lint > Naming*, *Acme Lint > Dependencies*. ## 2. Stable id naming Adopt a convention (kebab keys, category-level) and **forbid variable data in ids**: - Good: `ProblemId.create("unused-dependency", "Unused dependency", deps)` - Bad: `ProblemId.create("unused-${module}", ...)` — unique per occurrence, ungroupable. Treat the id catalog like an API: changing a key silently breaks IDE quick-fix mappings and scan trends. ## 3. Centralize injection Don't re-inject `Problems` in every class with ad-hoc call sites. Provide a shared base or helper: ```kotlin abstract class DiagnosticsSupport @Inject constructor( protected val problems: Problems ) { protected fun warn(id: ProblemId, label: String, detail: String? = null) = problems.reporter.report(id) { s -> s.contextualLabel(label).severity(Severity.WARNING) if (detail != null) s.details(detail) } } ``` This also **isolates the incubating API** (8.x): a signature change hits one file, not dozens. ## 4. Migrate incrementally For each `logger.warn(msg)`: 1. Pick/define the right `ProblemId`. 2. Move `msg` into `contextualLabel` (short) and any extra prose into `details`. 3. Add a **location** (file/line or task path) where you have it. 4. Choose severity: `ADVICE`/`WARNING` for `report`, `ERROR` + `throwing` only for true failures. Keep the old log line temporarily if needed, then remove it once consumers rely on the structured form. ## 5. Governance - Review new ids like API additions. - Keep ids stable across releases. - Document the taxonomy so other teams' plugins fit the same tree. ## Net effect Dozens of scattered, unstructured warnings become a **governed catalog** of problems that the console deduplicates, the HTML report organizes, IDEs annotate, and scans trend — consistently across every plugin in the org.
- Why centralize the Problems injection in a shared base?It standardizes how every plugin reports (consistent groups, severities, labels) and isolates the incubating 8.x API behind one seam, so an API change touches one file instead of every call site.
- Why treat ProblemId keys like a public API?Consumers map ids to dedup counts, IDE quick-fixes and scan trends. Renaming a key silently breaks those mappings and historical trends, just like changing a public method signature.
- How do you decide which warnings become errors during migration?Set org policy on severity: advisory/style issues stay report()+WARNING/ADVICE; only contract violations that invalidate the build become throwing()+ERROR, applied consistently across plugins.
It's like introducing an error-code catalog across microservices: the codes become a shared contract every dashboard and on-call runbook depends on, so you govern them, not mint them ad hoc.
saying these in an interview costs you the question
- Minting a fresh ProblemId per occurrence, defeating grouping.
- Scattering raw Problems calls everywhere with no shared convention.
- Renaming ids freely, breaking IDE/scan consumers that depend on them.