An Elm code generation library built on top of elm-codegen to help you generate feature flags without any boilerplate.
By "feature flags", we're talking about a record with a bunch of primitive types, mostly booleans. Something like:
type alias FeatureFlags =
{ animateSignInButton : Bool
, useNewFooter : Bool
}With minimal config, this package aims to make it easy (and safe!) to maintain, extend, and reduce these records without thinking hard (or, ideally, at all).
Like any Elm package, you're going to need Elm, and a project to install stuff in! After that:
codegen/ directory: elm install fakemonster/elm-codegen-feature-flagsLet's say you're working on a big new feature for your website. You've already written a ton of code, and you'd like to deploy it and let people take a look, but it's not ready for the general public yet.
A common convention is to use "feature flags": runtime configuration (usually passed as url parameters or json objects!) that you key off of to activate your alternative experience. They're usually pretty simple, and I often see them implemented as a big record of primitive values. Pretty much always booleans, sometimes a string for using a new API version or something.
Over time, especially on a big project, these records can get unwieldy:
type alias FeatureFlags =
{ customApiDomain : Maybe String
, eagerLoadInvites : Bool
, largeLoginButton : Bool
, largeSignupButton : Bool
, useExperimentalAnimationLibrary : Bool
-- we won't add a sixth one. right?
}And they can become error-prone when you've got to serialize and deserialize them, or you copy-paste a bit too much adding a new one:
decoder =
map5
(field "customApiDomain" (JD.maybe JD.string))
(field "eagerLoadInvites" JD.bool)
(field "largeSignupButton" JD.bool)
(field "largeLoginButton" JD.bool) -- wrong order!!
(field "experimentalAnimationLibary" JD.bool) -- typo!Now, in our applications we have ways of preventing many of these issues. We could use custom types instead of booleans, or tag all of them with phantom types to avoid mixups. We could use functions or clever type aliases to get the compiler to guide us through the decoder, encoder etc. We could write tests! And I strongly encourage you to do all that in the interesting parts of your application.
But feature flags aren't interesting! They're a common pattern, it's a pile of data for powering
if expressions, and the only thing custom to you is the field name, at the end of the day. So
why not codegen them?
Straightforwardly, this is a codegen module that can make you a big (or small!) record full of
Bools, Strings, and Maybe Strings. They'll be bundled with:
withJsonConverters)withUrlConverters)Used in elm-codegen, generally your code will look something like this:
module Generate exposing (main)
import Gen.CodeGen.Generate as Generate
import CodeGen.FeatureFlags as FF
main : Program {} () ()
main =
Generate.run
[ FF.fromFlags
[ ( "customApiDomain", FF.maybeString )
, ( "welcomeMessage", FF.string "Welcome!" )
, ( "eagerLoadInvites", FF.bool )
, ( "largeLoginButton", FF.bool )
, ( "largeSignupButton", FF.bool )
, ( "useExperimentalAnimationLibrary", FF.bool )
]
|> FF.withJsonConverters
|> FF.withUrlConverters
|> FF.withQueryKeyFormatter (\s -> "f-" ++ s)
|> FF.generate "MyFeatureFlags"
]This generates a module named MyFeatureFlags with:
FeatureFlags alias containing your given fields and typesThen you can use it wherever you like:
init : JD.Value -> ( Model, Cmd Msg )
init flags =
( { flags =
Json.Decode.decodeValue MyFeatureFlags.decoder flags
|> Result.withDefault MyFeatureFlags.default
}
, Cmd.none
)
view : Model -> Html Msg
view model =
div []
[ if model.flags.largeSignupButton then
button [ HE.onClick ClickedSignup, HA.class "button-large" ]
[ text "PLEASE click!" ]
else
button [ HE.onClick ClickedSignup, HA.class "button" ]
[ text "Click here!" ]
]If you're looking with a careful eye, what this package really does is automatic derivation for
JSON serialization, URL serialization, and a couple of custom concepts of "merging" (or) and
"iterating" (WhenApplied x), all limited to "records with a few primitive types".
The examples directory shows an application using FeatureFlags in practice. Clone this repo, and
then in the examples directory:
npx elm-codegen installnpx elm-codegen run --output generatedYou can also use elm-doc-preview to see documentation for the generated code as well! All these
commands are in the Makefile as well.
The code generated here is based on my experience with feature flags, and the types of transformation and maintenance that I've had to do with them. If you have other utilities you'd be interested in, or support for additional types, please open an issue to let me know.