Elm in Production

Hello friends,

Have been using Elm for a few years now and can’t imagine doing any front end without it. The most recent product is Digital Asset Exchange platform which is still on Beta.

https://digax.com/
https://exchange.digax.com/markets/btcltc

Both websites are written entirely in Elm with few ports and migrated from Ruby on Rails. Here are some good and bad points of using Elm in production.

Good

  • The maintainability and scalability is a breeze. But only if you structure application right.
  • No runtime exceptions.
  • Considerably less testing.
  • Caches lots of bugs coming from the backend
  • That 100% certainty that application works as it supposed to :slight_smile:

Bad

  • Have to write a lot of boilerplate especially when decoding JSON.
  • Initial development is much slower. But that is compensating with easy maintainability.
  • Long compilation times. Hopefully, it will get better after updating to 0.19.

Just wanted to share an experience. Let me know if you have any questions.

p.s. Can’t share the source code.

6 Likes

Hey, thank you for sharing!

Could you describe what the right structure is in your case? Are you doing child to parent communication and if yes, how?

I am curious to know if your types in elm are more or less the same structure as your json or do you use a lot of custom types?
Are you using any tools to generate the json decoders? From the backend, in the editor or with QuickType?

2 Likes

Is there any reason stopping you from porting to 0.19, except free time? What is the stack used, and what you used to develop the views, elm-css, elm-ui or plain css? Thanks for sharing.

What I use to escape boredom of writing JSON decoders is DerivingVia Haskell extension and Elm Bridge to generate JSON decoders/encoders. 1-2 lines and you have an Elm decoder :slight_smile:

3 Likes

Thanks, for showing an interest. I will try to answer questions as best as I can.

Could you describe what the right structure is in your case? Are you doing child to parent communication and if yes, how?

I found that deeply nested child to parent communication just complicates things and it becomes harder to refactor. Therefore, I would not use more than two tier of nesting. Top Tier TEA components act as proxies and all the logic resides in the Bottom Tier. This is how the folder structure looks like:

   - Type1
      // Bottom Tier
      - Messages.elm 
      - Model.elm 
      - Update.elm
      - View.elm
     // Helpers
      - Page.elm
      - API.elm 
      - Remote.elm 
      - Decoder.elm
      - Encoder.elm
        ...
   - Type2 
        ...
    // Top Tier
    Messages.elm 
    Model.elm
    Update.elm 
    View.elm 
    // Helpers
    Encoder.elm
    Decoder.elm
    ...
    // Main 
    Main.elm
  • A Type can be domain centered such as User, Blog, Article or smaller ones such as Date, Decimal, Avatar, or view centered such as Header, Footer, Dropdown, or more functional such as Sort, Filter, Route.

  • New Types are introduced when current Types start to share the same code, e.g. whether that would be a model, view or update.

  • Not all Types has all three TEA components. In many cases, they start very innocently, with just one Model or a View module, but with a time they mature. E.g.Header type may have only a View but later on, we may add a dropdown, and for that, it needs Model to contain a dropdown, Messages and an Update. Another example is Date type, which initially is just a Model, but if some Views in Types starts to share the same code involving Date, then we create a View module in the Date.

  • Helpers splits Update, Model and a View in respective tiers into smaller modules. E.g Model is split into Page and API. Page contains models from other types such as dropdown, pagination etc. and functions to update it. API contains HTTP results and commands to generate them. An update is split into Decoder, Encoder, the same can be said about Views.

  • Most of the function signatures in Model and helpers are in this format: Model -> a, and 99% they are used to update or transform a Model in respective Tier.

  • Update module functions in both tiers has this signature: BottomMsg -> TopModel -> (TopModel, Cmd Msg). In other words, every Update function in Bottom tier updates a Top tier Model based on the Bottom Tier msg. By allowing every Update module to access global data, we increase the extensibility of that module and avoid refactoring function signature when the module grows. This would be very bad in a language with mutable data, but in Elm this is not a case.

  • Initially Views in the Bottom tier has this signature: BottomModel -> View msg. But as they mature they end up with this TopModel -> View Msg signature.

  • Messages module contains messages for the respective tier. And in most cases, Bottom tier module contains only one, two or three messages. This is achieved,

    1. By passing a whole Bottom tier Model into a message, instead of fields for that Model. This has a big advantage of reducing the size of the update function, especially if we are updating TopModel which data is hidden by union types. The disadvantage is that if we are not careful, our Views can start to contain complicated logic, which is a big no-no. That’s why the logic for updating the Model resides in each Model respectively. In most cases, they are just Lenses. Views use them to create a message with updated Model.
    2. By having lots of small types. This again, allows us to maintain and extend the application more easily. One disadvantage of this is that Top tier modules grow fast and becomes big. But because they act as proxies, and doesn’t contain any logic, that doesn’t matter.

I am curious to know if your types in elm are more or less the same structure as your json or do you use a lot of custom types?

If I could guess, I would say that

  • 70% of all the types in the application are custom made such as Route, Dropdown, Pagination, Query etc.
  • 15% corresponds to JSON such as User, Order etc.
  • Because I am a big fan of Union types, 15% of types created for JSON are union types, E.g State, Compliance Level, for the User. And these types are the biggest pain to decode and to write modules for them. Especially if you want to make them opaque.

Are you using any tools to generate the json decoders? From the backend, in the editor or with QuickType?

No. Due to fact that I use a lot of Custom opaque types, these tools are not very useful. So I write them manually. The good thing is that, due to the architecture of the app, those Decoders are very composable.

Is there any reason stopping you from porting to 0.19, except free time?

Not particularly. Although, I have lots of svg strings embedded using innerHtml, so converting them in Svg will be a pain in the a**. In addition, I had doubts about migrating due to extensive usage of Highcharts.js, but libraries such as elm-visualization should help me.

What is the stack used, and what you used to develop the views, elm-css, elm-ui or plain css?

For views haven’t used any library, due to high customization requirements. As for CSS I have tried both elm-css and elm-ui. Both are exceptional libraries, unfortunately, any CSS library written in Elm suffers from the same problem - slow compilation. It just takes too long to compile and see the changes. Event 2s delay impacts productivity. Well at least in 0.18 version. Therefore, I use SASS. Due to the fact that SASS mixins are highly composable, they can correspond to Elm views. So if you have a Header view which contains a Button view in Elm then you have Header mixin which contains Button mixin in the SASS.

3 Likes

Regarding decoders, I create them from the swagger file using http://andreashultgren.se/swagger-elm/ works really well.

The encoders, I write manually, but they are easy to create once you have the decoders.

Regarding structure, https://github.com/rtfeldman/elm-spa-example is really good. I took me some time to understand all details of the sample, but once I did, everything is really simple. I have written to smaller administrative SPA applications with about 5000 lines each.

Hello @grrinchas, thanks for sharing your story!

A lot of people are using GraphQL with their Rails backends and dillonkearns/elm-graphql on the Elm-side to completely eliminate writing Json Decoders! Check it out! Feel free to discuss with us in the #graphql channel in the Elm slack, there are a lot of Rails users in there.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.