Kindlings

June 17, 2026 · View on GitHub

Kindlings logo

Kindlings

Type class derivation that compiles faster, runs faster, and works the same on Scala 2.13 and Scala 3. Drop-in replacements for derivation in Circe, Jsoniter Scala, Avro, and more — built on Hearth, powered by macros, free of the trade-offs you've learned to accept.

Why Kindlings?

Most Scala libraries derive type class instances using Shapeless (Scala 2), Scala 3 Mirrors, or Magnolia. These approaches work, but they come with trade-offs that compound as your project grows: slow compilation, poor error messages, runtime overhead from intermediate representations, and API fragmentation between Scala 2 and Scala 3.

Kindlings takes a different path. Built on Hearth, it uses macros to generate code that is closer to what you'd write by hand — while providing a better developer experience than any of the alternatives.

One API across Scala 2.13 and Scala 3

No conditional imports, no platform-specific code. Your derivation calls look the same regardless of the Scala version. Migration between Scala 2.13 and Scala 3 requires zero changes to your derivation code.

Automatic derivation without the usual cost

Traditional automatic derivation (à la import io.circe.generic.auto._) is notorious for slowing down compilation: the compiler re-derives instances at every use site, doing redundant work over and over. Semi-automatic derivation avoids this by caching instances as given/implicit vals — but at the cost of boilerplate.

Kindlings' sanely-automatic derivation gives you the best of both worlds. When a type is used in multiple places, the instance is derived once and reused. When a type is used in only one place, the derivation is effectively free — no different from having written it inline. The result: automatic-like convenience with semi-automatic-like performance.

Inlineable derivation

Need maximum performance? Kindlings lets you inline derivation directly at the call site, eliminating all abstraction overhead. The generated code is indistinguishable from what you'd write by hand.

Broad type support out of the box

Kindlings handles the types you actually use in real projects, without workarounds or extra configuration:

  • Case classes and sealed traits
  • Scala 3 enums and Java enums
  • Named tuples and opaque types
  • Scala and Java collections
  • Array and IArray
  • Recursive data types — no lazy wrappers, no manual knot-tying, no tricks. It just works.

Better compilation errors

When derivation fails, you shouldn't have to decode diverging implicit expansion or no implicit argument of type Encoder[???] was found. Kindlings produces clear, actionable error messages that tell you exactly which type is missing an instance and where in the type hierarchy the problem occurs.

Generated code preview

Curious what the macro actually produces? Kindlings lets you inspect the generated code, making debugging and optimization straightforward instead of a black-box guessing game.

Faster compilation, faster runtime

Kindlings aims to compile faster and run faster than derivation based on Shapeless, Mirrors, or Magnolia. There are no intermediate HList/Coproduct representations, no implicit search chains to resolve at compile time, and no runtime allocations for generic wrappers.

The one exception: Jsoniter Scala already generates highly optimized code via its own macros. For Jsoniter, we aim for parity — the same runtime speed, with the added benefits of cross-version compatibility and a unified API.

Comparison at a glance

KindlingsShapeless / MirrorsMagnoliaLibrary-specific macros
Same API on Scala 2.13 and 3varies
Auto derivation without overheadvaries
Inline derivationsome
Recursive types (no tricks)needs Lazy / workaroundsvaries
Clear error messagespartialvaries
Code previewrare
Named tuples, opaque typesrare
Scala 3 enums, Java enumspartialpartialvaries

For a deeper dive into why mainstream derivation approaches are suboptimal and how sanely-automatic derivation addresses their shortcomings, see Sanely-automatic derivation.

Available modules

ModuleReplacesDerived type classes
kindlings-avro-derivationavro4s (JVM only)AvroSchemaFor, AvroEncoder, AvroDecoder
kindlings-cats-derivationkittensShow, Eq, Order, Hash, Semigroup, Monoid, Functor, Foldable, Traverse, and 26 more
kindlings-circe-derivationcirce-generic-extras / circe configured derivationEncoder, Encoder.AsObject, Decoder
kindlings-diff-derivation(original)Diff (structural comparison with Myers diff)
kindlings-fast-show-pretty(original)FastShowPretty
kindlings-jsoniter-derivationjsoniter-scala JsonCodecMakerJsonValueCodec, JsonCodec, JsonKeyCodec
kindlings-pureconfig-derivationPureConfig generic (JVM only)KindlingsConfigReader, KindlingsConfigWriter, KindlingsConfigConvert
kindlings-scalacheck-derivationScalaCheck manual instancesArbitrary, Cogen, Shrink
kindlings-sconfig-derivation(original) — cross-platform HOCONConfigReader, ConfigWriter, ConfigCodec
kindlings-tapir-schema-derivationTapir built-in Schema.derivedSchema
kindlings-ubjson-derivation(original)UBJsonValueCodec
kindlings-xml-derivation(original)XmlEncoder, XmlDecoder
kindlings-yaml-derivationscala-yaml built-in derivesYamlEncoder, YamlDecoder

Integration modules

ModuleDescription
kindlings-cats-integrationNonEmptyList, NonEmptyVector, NonEmptyChain, Chain, NonEmptyMap, NonEmptySet, Validated — handled automatically in all derivation modules
kindlings-iron-integrationIron constrained types (`A :
kindlings-refined-integrationRefined types (Refined[A, P]) — validated on decode, unwrapped on encode

Macro utilities (not derivation)

Built on the same Hearth macro-agnostic API and cross-compiled the same way, but reimplementations of popular libraries from scratch (no dependency on the originals).

ModuleReimplementsWhat it does
kindlings-dimacwireCompile-time dependency injection — wire/autowire/wiredInModule, no reflection
kindlings-di-cats(original)Cats-Effect Resource/IO wiring on top of kindlings-di
kindlings-mockScalaMockCompile-time mocks (mock[T]) with expectations, no reflection or bytecode (test-scope)
kindlings-opticsquicklensobj.modify(_.a.b) lenses — nested copy/.each/.at/.when; .each works over any IsCollection/IsMap/IsOption container, incl. cats NonEmpty* via kindlings-cats-integration

Extra

ModuleDescription
kindlings-jsoniter-jsonMinimal JSON AST with optics and JsonValueCodec for jsoniter-scala
kindlings-tapir-openapi-jsoniterSerialize tapir-generated OpenAPI (sttp-apispec model) to JSON with jsoniter — no Circe dependency (JVM + Scala.js)

All modules are cross-compiled for Scala 2.13 and 3, on JVM, Scala.js, and Scala Native — except kindlings-avro-derivation and kindlings-pureconfig-derivation (JVM-only because their underlying libraries are JVM-only) and the kindlings-tapir-openapi-jsoniter tapir bridge (JVM + Scala.js, since tapir-openapi-docs is not published for Native; its underlying codecs remain cross-platform). Integration modules are add-and-forget: the macro extension system discovers them at compile time via SPI.

For a detailed feature-by-feature comparison of each module against the library it replaces, see the Feature Parity page in the documentation.

Why a separate project?

A natural question: why not contribute these derivation improvements directly to Circe, Jsoniter Scala, and other libraries?

Different foundations require different codebases. Kindlings' derivation is built on Hearth, a macro toolkit that provides high-level, cross-platform abstractions over Scala 2 and Scala 3 metaprogramming. Existing libraries use their own macro infrastructure (or Shapeless/Mirrors). Replacing the internals of a library with a completely different macro foundation is not a patch — it's a rewrite of the derivation layer, with different trade-offs, different error handling, and different supported types. That's not something you sneak into a PR.

Maintaining cross-version compatibility is a design constraint, not a feature flag. Most libraries have separate Scala 2 and Scala 3 implementations with different capabilities and sometimes different APIs. Kindlings is built from the ground up to share a single API and a single derivation logic across both versions. Retrofitting this onto a library that was designed differently would mean restructuring their entire build and module layout.

Independence allows faster iteration. Kindlings can support new type categories (named tuples, opaque types, Java enums), experiment with better error messages, and optimize compilation speed without being blocked by the release cycle or design philosophy of upstream libraries. If an approach proves successful here, it can inform upstream improvements — but it doesn't have to wait for consensus to ship.

Dogfooding Hearth in production conditions. Kindlings serves as a real-world stress test for Hearth's abstractions. If Hearth can't handle the complexity of deriving codecs for Circe, Avro, or Jsoniter Scala, that's a bug to fix before anyone else hits it. Every corner case discovered here makes Hearth more robust for all its users.

Learning by example. Kindlings doubles as a reference for how to use Hearth in practice — complete, tested derivation implementations that you can study, rather than piecing together usage from API docs alone.