Nine Guidelines for Modular Elm Development

Clean Code Lessons I’ve Learned

Why I Like Structure

I came to Elm after years of playing with mostly very opinionated frameworks. I love Ruby on Rails. The framework provides constraints that make it easy for me to maintain my own code. More importantly, the constraints provided by Rails allow me to jump into someone else’s code and pretty quickly see the lay of the land. I can start with the routes file to see what’s exposed to the outside world. I can look at the models to see the underlying data model. This is a great starting point for a large application. In a Rails project, there is no question of what goes where. Every piece of code has a pre-designated home. That removes both cognitive overload and debate on so many trivial issues.

Enter Elm

So is Elm opinionated? In many areas it is. It’s strongly typed and enforces the Redux-like pattern for managing state. These are constraints I heartily endorse as they result in cleaner and easier to debug code. But in other areas, Elm has no opinions. What should your application directory structure be? How should you split the model, update function and view structurally? When does it make sense to use subcomponents? These are all exercises left to the code writer.

As I started to create larger Elm applications, this bothered me. I wanted to think about the function of my code and use best practices to guide me to clean code structure. Also, being new to functional programming, I didn’t even have the object-oriented paradigm to guide me in arranging methods and state. I had a big bag of functions and no one to tell me how to break these bags into smaller bags that made sense.

Start with No Structure

In my earliest Elm programs, I just threw everything in one big file and got things working. That actually works pretty well. I was the sole developer and having everything together actually made for fast development. I could easily find anything using simple editor searches. I’d do that for a while until I got the the point where it just seemed too messy to show anyone. At the point where it became embarrassing to show off, I’d decide to start breaking things down.

Early Attempts at Restructuring

My early attempts at restructuring were haphazard at best. I’d first try to split out the model, view and update functionality into separate modules. I’d then try to further categorize the functions into buckets with related functionality. Along the way, I’d run into lost of problems. I’d find myself with modules that had circular dependencies and code that was even harder to find than when it was in one big file. I made lots of diagrams like this to try to help me out.

In the end, I usually came up with something that seemed reasonable, but in the end, I still had no rules to help me organize the next project any better. I still am a strong believer that the “one big file” approach is a great way to start off. But I really want some rules to help me do the restructuring.

Documenting the Structure

As I moved from the “one big file” to the “clean code” stage of my latest project, I decided to try to codify the rules I came up with as I restructured code. This post is an attempt to turn my findings into a set of rules I will use for future projects. I’m hoping that having a set of rules will allow me to start with a clean structure and avoid that big refactor as I modularize my code. I’ll next walk through the philosophies I’ve developed and the guidelines I use to enforce this philosophy.