Well-designed modules in Elm

What is a “well-designed” module in Elm?

How can we design modular interfaces to be clear, understandable, and strong?

Here are a few guidelines:

🧥 Don’t expose everything

A good module interface is intentional. So don’t use exposing (..).

Choose what should be exposed, and what shouldn’t.

☝️ Start with a type

Your module should be named after the type it’s “about”.

For instance, a Calendar.elm module should be about a Calendar datatype and its associated helper functions.

🧳 Avoid exposing all variants of a type

If client code is able to destructure your type or use variants directly, your interface will be less durable.

There are exceptions, but as a general rule, instead of Calendar(..), provide your interface through functions.

🎯 Functions should relate to your central type

If you find yourself writing functions prefixed with another type’s name (e.g. monthToString, monthFromDate), this is a likely sign that these belong with the Month type in in a new module.

🧠 Take advantage of conventions

Interfaces are forms of communication—be consistent with conventions that users of your code are likely to know!

For instance, In Elm, models are usually named Model, are initialized with an init function, updated with update, etc.

🍱 Organize with doc comments

Organizing your exposed types in logical groupings is a great way to help your code’s users get their bearings before they read the documentation.

elm-format arranges your exposures accordingly when you use @docs in your doc-comments.

This post was originally a Twitter thread as part of Ship 30 for 30.