Stories Earlier work, 2026
No ifs, no buts
Feature flags as interfaces and classes, so a call site says what it does and the compiler checks the rest
By Attila Fejér Published
Feature flags usually live in if-else blocks scattered across a codebase, keyed by strings nobody checks. I built FlagZen to move them out of sight: a flag becomes an interface, each variant a class, and generated code picks the active one. The code that uses a flag makes one method call, and the conditionals are hidden. The compiler checks what it can. It's open source, version 1.1.0 is on Maven Central. I'm not aware of any production uses yet.
- The idea
- a flag is an interface, each variant a class
- Checked at compile time
- variant values, types and duplicates
- Status
- open source, 1.1.0, no production users yet
Before
Feature flags are written the same way almost everywhere. Somewhere in the code, an if asks the flag provider for the value of a string key, and the two branches hold the old and the new behavior. A flag that matters shows up in many places, each with its own if. Retire the flag, and you hunt for each of them. Mistype the key, and nothing complains until the wrong branch runs.
The conditionals also bury the intent, and that bothered me most. To understand what a piece of code does, you first work out which branch applies, and the business logic hides between the flag checks. Nobody asked me to fix this, and feature flags work as they are. But I had written about replacing conditionals with polymorphism before, in a series on switch-case, and I asked why flags should be the exception.
What was built
FlagZen is a dispatch layer on top of the flag provider a team already uses, such as LaunchDarkly, OpenFeature, or Togglz. It doesn't manage flags, targeting, or A/B testing. Those are still the provider's responsibilities. The environment-variable and similar providers it ships are conveniences, not full flag providers.
A flag is a Java interface marked as a feature, and each variant is a class that implements it. At build time, an annotation processor generates the code that queries the provider for the current value and calls the matching class. The code that uses the flag gets an instance and calls a method on it. There is no if to read, and when the flag's value changes, the next call goes to the new variant, without a restart.
Because flags and variants are types, the compiler can check them. A variant whose value or type doesn't match its feature fails the build, and so do two variants with the same value. An unused variant gets a warning. Retiring a flag means deleting its interface, and the compiler points at every place that still uses it.
After
FlagZen is open source under the Apache 2.0 license. Version 1.1.0 is on Maven Central, with adapters for Spring Boot, OpenFeature, LaunchDarkly and Togglz. As far as I know, nobody uses it in production yet, and development continues. It's research and an aspiration: a way of writing feature flags I'd want in my own codebases, published so anyone can try it.
The lesson
If the compiler can check something, let it. A problem caught at build time is much cheaper to fix than one that reaches a user and causes them trouble. That's also why I prefer strongly typed languages.
And don't be afraid to question how things have always been done. If we never question our habits, even the ones that work, nothing new gets built.
More stories
More for engineering teams
Case study 2024 to 2026
XP coaching for 300+ Fortune 100 teams
Two years, about 1,000 people coached, and roughly 10% better lead time and change failure rate
How a Fortune 100 financial company moved its teams toward extreme programming: what two weeks in class and six at the desk changed, and which habits outlasted the coaches.
ReadEarlier work 2020 to 2022
Rebuilt while running
A monolith that archived tens of terabytes, moved piece by piece to microservices while it stayed live
How a system that archived and searched tens of terabytes of sensitive data moved from a monolith and a desktop client to microservices without a big-bang rewrite: the order, the numbers, and when I'd do it again.
ReadEarlier work 2017 to 2019
Divide and cache
A dashboard that took 10 seconds at 300 users, and 20 milliseconds at 2,000 after splitting a query into four
Just before a public-sector platform's first release, its dashboard couldn't take the load. How profiling found the cause, why indexing didn't fix it, and what did.
ReadWhere do conditionals hide the intent in your code?
A short conversation about where your code's design makes change hard. You leave with a recommendation for where to start.